c# - सूची<टी>से विरासत क्यों नहीं?




.net oop inheritance design (21)

What the guidelines say is that the public API should not reveal the internal design decision of whether you are using a list, a set, a dictionary, a tree or whatever. A "team" is not necessarily a list. You may implement it as a list but users of your public API should use you class on a need to know basis. This allows you to change your decision and use a different data structure without affecting the public interface.

मेरे कार्यक्रमों की योजना बनाते समय, मैं अक्सर इस तरह की विचारों की श्रृंखला से शुरू होता हूं:

एक फुटबॉल टीम सिर्फ फुटबॉल खिलाड़ियों की एक सूची है। इसलिए, मुझे इसका प्रतिनिधित्व करना चाहिए:

var football_team = new List<FootballPlayer>();

इस सूची का क्रम उस क्रम का प्रतिनिधित्व करता है जिसमें खिलाड़ियों को रोस्टर में सूचीबद्ध किया जाता है।

लेकिन मुझे बाद में एहसास हुआ कि खिलाड़ियों की सूची के अलावा टीमों के पास अन्य गुण भी हैं, जिन्हें रिकॉर्ड किया जाना चाहिए। उदाहरण के लिए, इस सीजन में कुल स्कोर, वर्तमान बजट, वर्दी रंग, टीम के नाम का प्रतिनिधित्व करने वाली एक string आदि ..

तो मुझे लगता है कि:

ठीक है, एक फुटबॉल टीम सिर्फ खिलाड़ियों की सूची की तरह है, लेकिन इसके अतिरिक्त, इसका नाम (एक string ) और स्कोरिंग कुल स्कोर (एक int ) है। .NET फुटबॉल टीमों को स्टोर करने के लिए कक्षा प्रदान नहीं करता है, इसलिए मैं अपनी कक्षा बनाउंगा। सबसे समान और प्रासंगिक मौजूदा संरचना List<FootballPlayer> , इसलिए मैं इससे प्राप्त करूंगा:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

लेकिन यह पता चला है कि एक दिशानिर्देश कहता है कि आपको List<T> से प्राप्त नहीं होना चाहिए । मैं इस दिशा में दो मामलों में पूरी तरह उलझन में हूं।

क्यों नहीं?

जाहिर है List किसी भी तरह प्रदर्शन के लिए अनुकूलित है । ऐसा कैसे? यदि मैं List विस्तार करता हूं तो मुझे किस प्रदर्शन समस्या का कारण होगा? वास्तव में क्या टूट जाएगा?

मैंने देखा है कि एक और कारण यह है कि List माइक्रोसॉफ्ट द्वारा प्रदान की जाती है, और मेरे पास इसका कोई नियंत्रण नहीं है, इसलिए मैं इसे "सार्वजनिक एपीआई" खोलने के बाद बाद में नहीं बदल सकता । लेकिन मैं इसे समझने के लिए संघर्ष करता हूं। एक सार्वजनिक एपीआई क्या है और मुझे क्यों परवाह करना चाहिए? अगर मेरी वर्तमान परियोजना में कभी भी इस सार्वजनिक एपीआई की संभावना नहीं है, तो क्या मैं इस दिशानिर्देश को सुरक्षित रूप से अनदेखा कर सकता हूं? अगर मैं List से प्राप्त करता हूं और यह पता चला है कि मुझे एक सार्वजनिक एपीआई चाहिए, तो मुझे क्या कठिनाइयों का सामना करना पड़ेगा?

इससे कोई फर्क क्यों पड़ता है? एक सूची एक सूची है। संभवतः क्या बदल सकता है? मैं संभवतः क्या बदलना चाहता हूं?

और आखिरकार, अगर माइक्रोसॉफ्ट नहीं चाहता था कि मैं List से उत्तराधिकारी हूं, तो उन्होंने वर्ग को sealed क्यों नहीं किया?

मुझे और क्या उपयोग करना चाहिए?

जाहिर है, कस्टम संग्रह के लिए, माइक्रोसॉफ्ट ने एक Collection वर्ग प्रदान किया है जिसे List बजाय बढ़ाया जाना चाहिए। लेकिन यह वर्ग बहुत नंगे है, और उदाहरण के लिए, AddRange जैसी कई उपयोगी चीजें नहीं हैं। jvitor83 का उत्तर उस विशेष विधि के लिए एक प्रदर्शन तर्क प्रदान करता है, लेकिन कोई AddRange कोई AddRange से बेहतर नहीं है?

Collection से विरासत List से विरासत से कहीं अधिक काम है, और मुझे कोई लाभ नहीं दिखता है। निश्चित रूप से माइक्रोसॉफ्ट मुझे किसी भी कारण से अतिरिक्त काम करने के लिए नहीं कहेंगे, इसलिए मैं महसूस नहीं कर सकता कि मैं किसी तरह से गलतफहमी कर रहा हूं, और विरासत Collection वास्तव में मेरी समस्या का सही समाधान नहीं है।

मैंने IList को लागू करने जैसे सुझाव देखे हैं। बस नहीं। यह बॉयलरप्लेट कोड की दर्जनों लाइनें हैं जो मुझे कुछ भी हासिल नहीं करती हैं।

अंत में, कुछ List में कुछ लपेटने का सुझाव देते हैं:

class FootballTeam 
{ 
    public List<FootballPlayer> Players; 
}

इसके साथ दो मुश्किलें हैं:

  1. यह मेरे कोड की जरूरत है वर्बोज़। अब मुझे my_team.Players.Count को केवल my_team.Count बजाय कॉल करना होगा। शुक्र है, सी # के साथ मैं सूचकांक को पारदर्शी बनाने और आंतरिक List सभी तरीकों को आगे बढ़ाने के लिए इंडेक्सर्स को परिभाषित कर सकता हूं ... लेकिन यह बहुत कोड है! मैं उस काम के लिए क्या प्राप्त करूं?

  2. यह सिर्फ सादा कोई समझ नहीं आता है। एक फुटबॉल टीम के पास खिलाड़ियों की सूची नहीं है। यह खिलाड़ियों की सूची है। आप यह नहीं कहते कि "जॉन मैकफूटबॉलर कुछ टीम के खिलाड़ियों में शामिल हो गया है"। आप कहते हैं "जॉन कुछ टीम में शामिल हो गया है"। आप "स्ट्रिंग के पात्रों" को एक अक्षर नहीं जोड़ते हैं, आप एक स्ट्रिंग में एक अक्षर जोड़ते हैं। आप पुस्तकालय की किताबों में कोई पुस्तक नहीं जोड़ते हैं, आप एक पुस्तकालय में एक पुस्तक जोड़ते हैं।

मुझे एहसास है कि "हुड के तहत" क्या होता है "एक्स को वाई की आंतरिक सूची जोड़ना" कहा जा सकता है, लेकिन यह दुनिया के बारे में सोचने के बहुत ही सहज ज्ञान युक्त तरीके की तरह लगता है।

मेरा प्रश्न (संक्षेप में)

डेटा संरचना का प्रतिनिधित्व करने का सही सी # तरीका क्या है, जो "तर्कसंगत" (जिसका अर्थ है, "मानव दिमाग में") कुछ घंटियों और सीटी के साथ things की एक list है?

List<T> से वंशानुगत है हमेशा अस्वीकार्य? यह कब स्वीकार्य है? क्यों नहीं? List<T> या नहीं से प्राप्त करने का निर्णय लेने पर प्रोग्रामर को क्या विचार करना चाहिए?


While I don't have a complex comparison as most of these answers do, I would like to share my method for handling this situation. By extending IEnumerable<T> , you can allow your Team class to support Linq query extensions, without publicly exposing all the methods and properties of List<T> .

class Team : IEnumerable<Player>
{
    private readonly List<Player> playerList;

    public Team()
    {
        playerList = new List<Player>();
    }

    public Enumerator GetEnumerator()
    {
        return playerList.GetEnumerator();
    }

    ...
}

class Player
{
    ...
}

My dirty secret: I don't care what people say, and I do it. .NET Framework is spread with "XxxxCollection" (UIElementCollection for top of my head example).

So what stops me saying:

team.Players.ByName("Nicolas")

When I find it better than

team.ByName("Nicolas")

Moreover, my PlayerCollection might be used by other class, like "Club" without any code duplication.

club.Players.ByName("Nicolas")

Best practices of yesterday, might not be the one of tomorrow. There is no reason behind most best practices, most are only wide agreement among the community. Instead of asking the community if it will blame you when you do that ask yourself, what is more readable and maintainable?

team.Players.ByName("Nicolas") 

या

team.ByName("Nicolas")

वास्तव में। Do you have any doubt? Now maybe you need to play with other technical constraints that prevent you to use List<T> in your real use case. But don't add a constraint that should not exist. If Microsoft did not document the why, then it is surely a "best practice" coming from nowhere.


A football team is not a list of football players. A football team is composed of a list of football players!

This is logically wrong:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

and this is correct:

class FootballTeam 
{ 
    public List<FootballPlayer> players
    public string TeamName; 
    public int RunningTotal 
}

क्या होगा यदि FootballTeam टीम के पास मुख्य टीम के साथ रिजर्व टीम है?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
}

आप इसे कैसे मॉडल करेंगे?

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

रिश्ते स्पष्ट रूप से एक है और नहीं है

या RetiredPlayers ?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
    List<FootballPlayer> RetiredPlayers { get; set; }
}

अंगूठे के नियम के रूप में, यदि आप कभी भी संग्रह से उत्तराधिकारी बनना चाहते हैं, तो वर्ग SomethingCollection नाम दें।

क्या आपका SomethingCollection अर्थपूर्ण रूप से समझ में आता है? केवल तभी ऐसा करें यदि आपका प्रकार Something संग्रह है।

FootballTeam के मामले में यह सही नहीं लगता है। एक Team एक Collection से अधिक है। एक Team पास कोच, ट्रेनर इत्यादि हो सकते हैं क्योंकि अन्य उत्तरों ने इंगित किया है।

FootballCollection संग्रह की तरह लगता है या शायद फुटबॉल सामग्री का संग्रह। TeamCollection , टीमों का संग्रह।

FootballPlayerCollection खिलाड़ियों के संग्रह की तरह लगता है जो एक वर्ग के लिए वैध नाम होगा जो List<FootballPlayer> से विरासत में मिलता है यदि आप वास्तव में ऐसा करना चाहते हैं।

वास्तव में List<FootballPlayer> से निपटने के लिए एक बिल्कुल अच्छा प्रकार है। शायद IList<FootballPlayer> यदि आप इसे किसी विधि से वापस कर रहे हैं।

संक्षेप में

अपने आप से पूछो

  1. X Y ? या X एक है?

  2. क्या मेरे वर्ग के नाम का मतलब है कि वे क्या हैं?


I think I don't agree with your generalization. A team isn't just a collection of players. A team has so much more information about it - name, emblem, collection of management/admin staff, collection of coaching crew, then collection of players. So properly, your FootballTeam class should have 3 collections and not itself be a collection; if it is to properly model the real world.

You could consider a PlayerCollection class which like the Specialized StringCollection offers some other facilities - like validation and checks before objects are added to or removed from the internal store.

Perhaps, the notion of a PlayerCollection betters suits your preferred approach?

public class PlayerCollection : Collection<Player> 
{ 
}

And then the FootballTeam can look like this:

public class FootballTeam 
{ 
    public string Name { get; set; }
    public string Location { get; set; }

    public ManagementCollection Management { get; protected set; } = new ManagementCollection();

    public CoachingCollection CoachingCrew { get; protected set; } = new CoachingCollection();

    public PlayerCollection Players { get; protected set; } = new PlayerCollection();
}

Let me rewrite your question. so you might see the subject from a different perspective.

When I need to represent a football team, I understand that it is basically a name. Like: "The Eagles"

string team = new string();

Then later I realized teams also have players.

Why can't I just extend the string type so that it also holds a list of players?

Your point of entry into the problem is arbitrary. Try to think what does a team have (properties), not what it is .

After you do that, you could see if it shares properties with other classes. And think about inheritance.


अंत में, कुछ सूची में कुछ लपेटने का सुझाव देते हैं:

यह सही तरीका है। "अनावश्यक शब्द" यह देखने का एक बुरा तरीका है। जब आप my_team.Players.Count लिखते हैं तो my_team.Players.Count स्पष्ट अर्थ होता है। आप खिलाड़ियों को गिनना चाहते हैं।

my_team.Count

..मतलब कुछ नहीं। क्या गिनती है?

एक टीम एक सूची नहीं है - इसमें खिलाड़ियों की एक सूची से अधिक शामिल है। एक टीम खिलाड़ियों का मालिक है, इसलिए खिलाड़ियों का हिस्सा होना चाहिए (एक सदस्य)।

यदि आप वास्तव में अत्यधिक वर्बोज़ होने के बारे में चिंतित हैं, तो आप हमेशा टीम से गुणों का पर्दाफाश कर सकते हैं:

public int PlayerCount {
    get {
        return Players.Count;
    }
}

.. जो बनता है:

my_team.PlayerCount

इसका अर्थ अब है और डेमेटर के कानून का पालन ​​करता है।

आपको समग्र पुन: उपयोग सिद्धांत का पालन करने पर भी विचार करना चाहिए। List<T> से विरासत में, आप कह रहे हैं कि एक टीम खिलाड़ियों की एक सूची है और इसके बाहर की जरूरतहीन विधियों को उजागर कर रही है। यह गलत है - जैसा कि आपने कहा है, एक टीम खिलाड़ियों की एक सूची से अधिक है: इसमें एक नाम, प्रबंधक, बोर्ड सदस्य, प्रशिक्षकों, चिकित्सा कर्मचारी, वेतन कैप्स इत्यादि हैं। आपकी टीम कक्षा में खिलाड़ियों की एक सूची है, आप कह रहे हैं "एक टीम के पास खिलाड़ियों की एक सूची है", लेकिन इसमें अन्य चीजें भी हो सकती हैं।


Does allowing people to say

myTeam.subList(3, 5);

make any sense at all? If not then it shouldn't be a List.


This reminds me of the "Is a" versus "has a" tradeoff. Sometimes it is easier and makesmore sense to inherit directly from a super class. Other times it makes more sense to create a standalone class and include the class you would have inherited from as a member variable. You can still access the functionality of the class but are not bound to the interface or any other constraints that might come from inheriting from the class.

Which do you do? As with a lot of things...it depends on the context. The guide I would use is that in order to inherit from another class there truly should be an "is a" relationship. So if you a writing a class called BMW, it could inherit from Car because a BMW truly is a car. A Horse class can inherit from the Mammal class because a horse actually is a mammal in real life and any Mammal functionality should be relevant to Horse. But can you say that a team is a list? From what I can tell, it does not seem like a Team really "is a" List. So in this case, I would have a List as a member variable.


When they say List<T> is "optimized" I think they want to mean that it doesn't have features like virtual methods which are bit more expensive. So the problem is that once you expose List<T> in your public API , you loose ability to enforce business rules or customize its functionality later. But if you are using this inherited class as internal within your project (as opposed to potentially exposed to thousands of your customers/partners/other teams as API) then it may be OK if it saves your time and it is the functionality you want to duplicate. The advantage of inheriting from List<T> is that you eliminate lot of dumb wrapper code that is just never going to be customized in foreseeable future. Also if you want your class to explicitly have exact same semantics as List<T> for the life of your APIs then also it may be OK.

I often see lot of people doing tons of extra work just because of FxCop rule says so or someone's blog says it's a "bad" practice. Many times, this turns code in to design pattern palooza weirdness. As with lot of guideline, treat it as guideline that can have exceptions.


It depends on the behaviour of your "team" object. If it behaves just like a collection, it might be OK to represent it first with a plain List. Then you might start to notice that you keep duplicating code that iterates on the list; at this point you have the option of creating a FootballTeam object that wraps the list of players. The FootballTeam class becomes the home for all the code that iterates on the list of players.

It makes my code needlessly verbose. I must now call my_team.Players.Count instead of just my_team.Count. Thankfully, with C# I can define indexers to make indexing transparent, and forward all the methods of the internal List... But that's a lot of code! What do I get for all that work?

Encapsulation. Your clients need not know what goes on inside of FootballTeam. For all your clients know, it might be implemented by looking the list of players up in a database. They don't need to know, and this improves your design.

It just plain doesn't make any sense. A football team doesn't "have" a list of players. It is the list of players. You don't say "John McFootballer has joined SomeTeam's players". You say "John has joined SomeTeam". You don't add a letter to "a string's characters", you add a letter to a string. You don't add a book to a library's books, you add a book to a library.

Exactly :) you will say footballTeam.Add(john), not footballTeam.List.Add(john). The internal list will not be visible.


वाह, आपकी पोस्ट में पूरी तरह से प्रश्न और अंक हैं। माइक्रोसॉफ्ट से आपको जो तर्क मिलता है वह बिल्कुल बिंदु पर है। आइए List<T> बारे में सब कुछ शुरू करें

  • List<T> अत्यधिक अनुकूलित है। इसका मुख्य उपयोग किसी ऑब्जेक्ट के निजी सदस्य के रूप में उपयोग किया जाना है।
  • माइक्रोसॉफ्ट ने इसे सील नहीं किया क्योंकि कभी-कभी आप एक क्लास बनाना चाहते हैं जिसमें मित्रतापूर्ण नाम हो: class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... } । अब यह var list = new MyList<int, string>(); के रूप में आसान है var list = new MyList<int, string>();
  • सीए 1002: जेनेरिक सूचियों का पर्दाफाश न करें : असल में, यदि आप इस ऐप को एकमात्र डेवलपर के रूप में उपयोग करने की योजना बना रहे हैं, तो भी अच्छे कोडिंग प्रथाओं के साथ विकसित करना फायदेमंद है, इसलिए वे आपके और दूसरी प्रकृति में शामिल हो जाते हैं। यदि आपको किसी भी उपभोक्ता को अनुक्रमित सूची रखने की आवश्यकता है, तो आपको अभी भी एक IList<T> रूप में सूची का पर्दाफाश करने की अनुमति है। यह आप बाद में कक्षा के भीतर कार्यान्वयन को बदलते हैं।
  • माइक्रोसॉफ्ट ने Collection<T> बहुत सामान्य बना दिया क्योंकि यह एक सामान्य अवधारणा है ... नाम यह सब कहता है; यह सिर्फ एक संग्रह है। SortedCollection<T> , SortedCollection<T> , SortedCollection<T> जैसे अधिक सटीक संस्करण हैं जिनमें से SortedCollection<T> लागू नहीं करता है लेकिन List<T> लागू नहीं करता है
  • Collection<T> सदस्यों के लिए अनुमति देता है (यानी जोड़ें, निकालें, इत्यादि) ओवरराइड होने के कारण वे वर्चुअल हैं। List<T> नहीं है।
  • आपके प्रश्न का अंतिम भाग स्पॉट पर है। एक फुटबॉल टीम सिर्फ खिलाड़ियों की एक सूची से अधिक है, इसलिए यह एक वर्ग होना चाहिए जिसमें खिलाड़ियों की सूची शामिल हो। रचना बनाम विरासत सोचो। एक फुटबॉल टीम में खिलाड़ियों की एक सूची होती है (एक रोस्टर), यह खिलाड़ियों की एक सूची नहीं है।

अगर मैं इस कोड को लिख रहा था तो कक्षा शायद ऐसा कुछ दिखाई देगी:

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    // Yes. I used LINQ here. This is so I don't have to worry about
    // _roster.Length vs _roster.Count vs anything else.
    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

सबसे पहले, इसे उपयोगिता के साथ करना है। यदि आप विरासत का उपयोग करते हैं, तो Team क्लास व्यवहार (विधियों) का पर्दाफाश करेगी जो पूरी तरह से ऑब्जेक्ट मैनिपुलेशन के लिए डिज़ाइन की गई हैं। उदाहरण के लिए, AsReadOnly() या CopyTo(obj) विधियों को टीम ऑब्जेक्ट के लिए कोई समझ नहीं आता है। AddRange(items) विधि के बजाय आप शायद अधिक वर्णनात्मक AddPlayers(players) विधि चाहते हैं।

यदि आप LINQ का उपयोग करना चाहते हैं, तो एक सामान्य इंटरफेस को लागू करना जैसे कि ICollection<T> या IEnumerable<T> अधिक समझ में आता है।

जैसा कि बताया गया है, संरचना इसके बारे में जाने का सही तरीका है। बस एक निजी चर के रूप में खिलाड़ियों की एक सूची लागू करें।


class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal;
}

पिछला कोड का मतलब है: सड़क से लड़ने वाले लोगों का एक समूह फुटबॉल खेल रहा है, और उनके पास एक नाम होना है। कुछ इस तरह:

वैसे भी, यह कोड (मेरे उत्तर से)

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    public int PlayerCount
    {
    get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

मतलब: यह एक फुटबॉल टीम है जिसमें प्रबंधन, खिलाड़ी, प्रशासक इत्यादि हैं। कुछ ऐसा है:

चित्रों में आपका तर्क प्रस्तुत किया गया है ...


यहां कुछ अच्छे जवाब दिए गए हैं। मैं उन्हें निम्नलिखित बिंदु जोड़ दूंगा।

डेटा संरचना का प्रतिनिधित्व करने का सही सी # तरीका क्या है, जो "तर्कसंगत" (जिसका अर्थ है, "मानव दिमाग में") कुछ घंटियों और सीटी के साथ चीजों की एक सूची है?

रिक्त स्थान भरने के लिए फुटबॉल के अस्तित्व से परिचित किसी भी दस गैर कंप्यूटर-प्रोग्रामर लोगों से पूछें:

A football team is a particular kind of _____

क्या किसी ने "कुछ घंटियों और सीटी के साथ फुटबॉल खिलाड़ियों की सूची" कहा, या क्या वे सभी "स्पोर्ट्स टीम" या "क्लब" या "संगठन" कहते थे? आपकी धारणा है कि एक फुटबॉल टीम खिलाड़ियों की एक विशेष प्रकार की सूची है जो आपके मानव दिमाग में और आपके मानव दिमाग में अकेली है।

List<T> एक तंत्र है । फुटबॉल टीम एक व्यावसायिक वस्तु है - यानी, एक ऐसी वस्तु जो कुछ अवधारणा का प्रतिनिधित्व करती है जो कार्यक्रम के व्यावसायिक डोमेन में है। उनको मिश्रण मत करो! एक फुटबॉल टीम एक तरह की टीम है; इसमें रोस्टर होता है, रोस्टर खिलाड़ियों की एक सूची है । एक रोस्टर खिलाड़ियों की एक विशेष प्रकार की सूची नहीं है। एक रोस्टर खिलाड़ियों की एक सूची है। तो Roster नामक एक संपत्ति बनाएं जो एक List<Player> । और जब आप उस पर हों, तब तक इसे ReadOnlyList<Player> बनाएं, जब तक कि आप विश्वास न करें कि फुटबॉल टीम के बारे में जो भी जानता है वह रोस्टर से खिलाड़ियों को हटा देता है।

List<T> से वंशानुगत है हमेशा अस्वीकार्य?

किसके लिए अस्वीकार्य? मेरे? नहीं।

यह कब स्वीकार्य है?

जब आप एक तंत्र बना रहे हैं जो List<T> तंत्र को बढ़ाता है

List<T> या नहीं से प्राप्त करने का निर्णय लेने पर प्रोग्रामर को क्या विचार करना चाहिए?

क्या मैं एक तंत्र या व्यापार वस्तु बना रहा हूं?

लेकिन यह बहुत सारे कोड है! मैं उस काम के लिए क्या प्राप्त करूं?

आपने अपना प्रश्न टाइप करने में अधिक समय बिताया है कि यह आपको List<T> पचास गुना ओवर के प्रासंगिक सदस्यों के लिए अग्रेषण विधियों को लिखने के लिए ले जाएगा। आप स्पष्ट रूप से शब्दशः से डरते नहीं हैं, और हम यहां बहुत कम मात्रा में कोड के बारे में बात कर रहे हैं; यह कुछ मिनट काम है।

अद्यतन करें

मैंने इसे और अधिक सोचा और खिलाड़ियों की एक सूची के रूप में फुटबॉल टीम का मॉडल नहीं करने का एक और कारण है। वास्तव में खिलाड़ियों की एक सूची के रूप में फुटबॉल टीम का मॉडल करना भी एक बुरा विचार हो सकता है। खिलाड़ियों की सूची के रूप में एक टीम के साथ समस्या यह है कि आपको जो कुछ मिला है वह समय में एक पल में टीम का एक स्नैपशॉट है। मुझे नहीं पता कि इस वर्ग के लिए आपका व्यावसायिक मामला क्या है, लेकिन अगर मेरे पास एक फुटबॉल टीम का प्रतिनिधित्व करने वाली कक्षा थी, तो मैं इसे पूछना चाहूंगा कि "2003 और 2013 के बीच चोट के कारण कितने सेहॉक्स खिलाड़ी खेल चूक गए?" या "डेनवर खिलाड़ी जो पहले किसी अन्य टीम के लिए खेले थे, साल में सालाना सालाना बढ़ोतरी हुई थी?" या " क्या पिगर्स इस साल सभी तरह से जाते थे? "

यही है, एक फुटबॉल टीम मुझे ऐतिहासिक तथ्यों के संग्रह के रूप में अच्छी तरह से तैयार करने के लिए प्रतीत होती है जैसे कि जब एक खिलाड़ी भर्ती, घायल, सेवानिवृत्त, इत्यादि। स्पष्ट रूप से वर्तमान खिलाड़ी रोस्टर एक महत्वपूर्ण तथ्य है जो शायद सामने और- केंद्र, लेकिन ऐसी अन्य रोचक चीजें हो सकती हैं जो आप इस ऑब्जेक्ट के साथ करना चाहते हैं जिसके लिए एक और ऐतिहासिक परिप्रेक्ष्य की आवश्यकता है।


जैसा कि सभी ने इंगित किया है, खिलाड़ियों की एक टीम खिलाड़ियों की एक सूची नहीं है। यह गलती हर जगह कई लोगों द्वारा बनाई गई है, शायद विशेषज्ञता के विभिन्न स्तरों पर। अक्सर इस मामले में समस्या सूक्ष्म और कभी-कभी बहुत सकल होती है। ऐसे डिज़ाइन खराब हैं क्योंकि ये लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन करते हैं। इस अवधारणा को समझाने वाले इंटरनेट में कई अच्छे लेख हैं, उदाहरण के लिए, http://en.wikipedia.org/wiki/Liskov_substitution_principle

संक्षेप में, कक्षाओं के बीच अभिभावक / बाल संबंध में दो नियम संरक्षित किए जाने हैं:

  • एक बच्चे को पूरी तरह से माता-पिता को परिभाषित करने से कम कोई विशेषता नहीं होनी चाहिए।
  • बच्चे को पूरी तरह से परिभाषित करने के अलावा माता-पिता को कोई विशेषता नहीं चाहिए।

दूसरे शब्दों में, एक माता-पिता एक बच्चे की एक आवश्यक परिभाषा है, और एक बच्चा माता-पिता की पर्याप्त परिभाषा है।

यहां समाधान समाधान के माध्यम से सोचने का एक तरीका है और उपर्युक्त सिद्धांत लागू करें जो किसी को ऐसी गलती से बचने में मदद करे। किसी को यह सत्यापित करके किसी परिकल्पना का परीक्षण करना चाहिए कि क्या माता-पिता वर्ग के सभी संचालन व्युत्पन्न वर्ग के लिए संरचनात्मक रूप से और अर्थात् दोनों के लिए मान्य हैं।

  • क्या फुटबॉल टीम फुटबॉल खिलाड़ियों की एक सूची है? (एक सूची के सभी गुण एक ही अर्थ में एक टीम पर लागू होते हैं)
    • क्या एक टीम समरूप संस्थाओं का संग्रह है? हां, टीम खिलाड़ियों का संग्रह है
    • टीम के राज्य के वर्णनात्मक खिलाड़ियों को शामिल करने का आदेश है और क्या टीम यह सुनिश्चित करती है कि अनुक्रम सुरक्षित रूप से परिवर्तित होने तक संरक्षित है? नहीं, और नहीं
    • खिलाड़ियों को टीम में अपनी अनुक्रमिक स्थिति के आधार पर शामिल / गिराए जाने की उम्मीद है? नहीं

जैसा कि आप देखते हैं, केवल एक सूची की पहली विशेषता एक टीम के लिए लागू होती है। इसलिए एक टीम एक सूची नहीं है। एक सूची एक कार्यान्वयन विस्तार होगी कि आप अपनी टीम को कैसे प्रबंधित करते हैं, इसलिए इसका उपयोग केवल प्लेयर ऑब्जेक्ट्स को स्टोर करने और टीम क्लास के तरीकों से छेड़छाड़ करने के लिए किया जाना चाहिए।

इस बिंदु पर मैं टिप्पणी करना चाहता हूं कि एक टीम वर्ग को मेरी राय में, सूची का उपयोग करके भी लागू नहीं किया जाना चाहिए; इसे ज्यादातर मामलों में एक सेट डेटा संरचना (उदाहरण के लिए हैशसेट) का उपयोग करके कार्यान्वित किया जाना चाहिए।


What is the correct C# way of representing a data structure...

Remeber, "All models are wrong, but some are useful." - George EP Box

There is no a "correct way", only a useful one.

Choose one that is useful to you and/your users. बस। Develop economically, don't over-engineer. The less code you write, the less code you will need to debug. (read the following editions).

-- Edited

My best answer would be... it depends. Inheriting from a List would expose the clients of this class to methods that may be should not be exposed, primarily because FootballTeam looks like a business entity.

-- Edition 2

I sincerely don't remember to what I was referring on the “don't over-engineer” comment. While I believe the KISS mindset is a good guide, I want to emphasize that inheriting a business class from List would create more problems than it resolves, due abstraction leakage .

On the other hand, I believe there are a limited number of cases where simply to inherit from List is useful. As I wrote in the previous edition, it depends. The answer to each case is heavily influenced by both knowledge, experience and personal preferences.

Thanks to @kai for helping me to think more precisely about the answer.


Just because I think the other answers pretty much go off on a tangent of whether a football team "is-a" List<FootballPlayer> or "has-a" List<FootballPlayer> , which really doesn't answer this question as written.

The OP chiefly asks for clarification on guidelines for inheriting from List<T> :

A guideline says that you shouldn't inherit from List<T> . Why not?

Because List<T> has no virtual methods. This is less of a problem in your own code, since you can usually switch out the implementation with relatively little pain - but can be a much bigger deal in a public API.

What is a public API and why should I care?

A public API is an interface you expose to 3rd party programmers. Think framework code. And recall that the guidelines being referenced are the ".NET Framework Design Guidelines" and not the ".NET Application Design Guidelines". There is a difference, and - generally speaking - public API design is a lot more strict.

If my current project does not and is not likely to ever have this public API, can I safely ignore this guideline? If I do inherit from List and it turns out I need a public API, what difficulties will I have?

Pretty much, yeah. You may want to consider the rationale behind it to see if it applies to your situation anyway, but if you're not building a public API then you don't particularly need to worry about API concerns like versioning (of which, this is a subset).

If you add a public API in the future, you will either need to abstract out your API from your implementation (by not exposing your List<T> directly) or violate the guidelines with the possible future pain that entails.

Why does it even matter? A list is a list. What could possibly change? What could I possibly want to change?

Depends on the context, but since we're using FootballTeam as an example - imagine that you can't add a FootballPlayer if it would cause the team to go over the salary cap. A possible way of adding that would be something like:

 class FootballTeam : List<FootballPlayer> {
     override void Add(FootballPlayer player) {
        if (this.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
        }
     }
 }

Ah...but you can't override Add because it's not virtual (for performance reasons).

If you're in an application (which, basically, means that you and all of your callers are compiled together) then you can now change to using IList<T> and fix up any compile errors:

 class FootballTeam : IList<FootballPlayer> {
     private List<FootballPlayer> Players { get; set; }

     override void Add(FootballPlayer player) {
        if (this.Players.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
        }
     }
     /* boiler plate for rest of IList */
 }

but, if you've publically exposed to a 3rd party you just made a breaking change that will cause compile and/or runtime errors.

TL;DR - the guidelines are for public APIs. For private APIs, do what you want.


I just wanted to add that Bertrand Meyer, the inventor of Eiffel and design by contract, would have Team inherit from List<Player> without so much as batting an eyelid.

In his book, Object-Oriented Software Construction , he discusses the implementation of a GUI system where rectangular windows can have child windows. He simply has Window inherit from both Rectangle and Tree<Window> to reuse the implementation.

However, C# is not Eiffel. The latter supports multiple inheritance and renaming of features . In C#, when you subclass, you inherit both the interface and the implemenation. You can override the implementation, but the calling conventions are copied directly from the superclass. In Eiffel, however, you can modify the names of the public methods, so you can rename Add and Remove to Hire and Fire in your Team . If an instance of Team is upcast back to List<Player> , the caller will use Add and Remove to modify it, but your virtual methods Hire and Fire will be called.


असल में आप जो चाहते हैं उसे प्राप्त करने के लिए एक इंटरफ़ेस का उपयोग कर सकते हैं।

public interface Animal {
    String getName();
    String getVoice();
}
public class Dog implements Animal{
    @Override 
    String getName(){return "Dog";}
    @Override
    String getVoice(){return "woof!";}

}

फिर आप संग्रह का उपयोग कर सकते हैं

List <Animal> animalGroup = new ArrayList<Animal>();
animalGroup.add(new Dog());




c# .net oop inheritance design