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




.net oop (17)

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

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

my_team.Count

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

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

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

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

.. जो बनता है:

my_team.PlayerCount

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

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

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

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

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> या नहीं से प्राप्त करने का निर्णय लेने पर प्रोग्रामर को क्या विचार करना चाहिए?


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.


डिजाइन> कार्यान्वयन

आप किस तरीके और संपत्ति का पर्दाफाश करते हैं वह एक डिज़ाइन निर्णय है। आप किस आधार वर्ग से उत्तराधिकारी हैं, एक कार्यान्वयन विस्तार है। मुझे लगता है कि पूर्व में एक कदम वापस लेने लायक है।

एक वस्तु डेटा और व्यवहार का संग्रह है।

तो आपके पहले प्रश्न होना चाहिए:

  • मेरे द्वारा बनाए जा रहे मॉडल में यह ऑब्जेक्ट किस डेटा में शामिल है?
  • इस मॉडल में इस ऑब्जेक्ट का प्रदर्शन क्या व्यवहार करता है?
  • भविष्य में यह परिवर्तन कैसे हो सकता है?

ध्यान रखें कि विरासत एक "आईएसए" (ए) संबंध का तात्पर्य है, जबकि रचना का अर्थ है "है" (हैसा) संबंध है। अपने विचार में अपनी स्थिति के लिए सही चुनें, ध्यान में रखें कि आपके एप्लिकेशन के विकास के साथ चीजें कहां जा सकती हैं।

Consider thinking in interfaces before you think in concrete types, as some people find it easier to put their brain in "design mode" that way.

This isn't something everyone does consciously at this level in day to day coding. But if you're mulling this sort of topic, you're treading in design waters. Being aware of it can be liberating.

Consider Design Specifics

Take a look at List<T> and IList<T> on MSDN or Visual Studio. See what methods and properties they expose. Do these methods all look like something someone would want to do to a FootballTeam in your view?

Does footballTeam.Reverse() make sense to you? Does footballTeam.ConvertAll<TOutput>() look like something you want?

This isn't a trick question; the answer might genuinely be "yes". If you implement/inherit List<Player> or IList<Player>, you're stuck with them; if that's ideal for your model, do it.

If you decide yes, that makes sense, and you want your object to be treatable as a collection/list of players (behaviour), and you therefore want to implement ICollection or IList, by all means do so. Notionally:

class FootballTeam : ... ICollection<Player>
{
    ...
}

If you want your object to contain a collection/list of players (data), and you therefore want the collection or list to be a property or member, by all means do so. Notionally:

class FootballTeam ...
{
    public ICollection<Player> Players { get { ... } }
}

You might feel that you want people to be able to only enumerate the set of players, rather than count them, add to them or remove them. IEnumerable<Player> is a perfectly valid option to consider.

You might feel that none of these interfaces are useful in your model at all. This is less likely (IEnumerable<T> is useful in many situations) but it's still possible.

Anyone who attempts to tell you that one of these it is categorically and definitively wrong in every case is misguided. Anyone who attempts to tell you it is categorically and definitively right in every case is misguided.

Move on to Implementation

Once you've decided on data and behaviour, you can make a decision about implementation. This includes which concrete classes you depend on via inheritance or composition.

This may not be a big step, and people often conflate design and implementation since it's quite possible to run through it all in your head in a second or two and start typing away.

A Thought Experiment

An artificial example: as others have mentioned, a team is not always "just" a collection of players. Do you maintain a collection of match scores for the team? Is the team interchangable with the club, in your model? If so, and if your team isa collection of players, perhaps it also isa collection of staff and/or a collection of scores. Then you end up with:

class FootballTeam : ... ICollection<Player>, 
                         ICollection<StaffMember>,
                         ICollection<Score>
{
    ....
}

Design notwithstanding, at this point in C# you won't be able to implement all of these by inheriting from List<T> anyway, since C# "only" supports single inheritance. (If you've tried this malarky in C++, you may consider this a Good Thing.) Implementing one collection via inheritance and one via composition is likely to feel dirty. And properties such as Count become confusing to users unless you implement ILIst<Player>.Count and IList<StaffMember>.Count etc. explicitly, and then they're just painful rather than confusing. You can see where this is going; gut feeling whilst thinking down this avenue may well tell you it feels wrong to head in this direction (and rightly or wrongly, your colleagues might also if you implemented it this way!)

The Short Answer (Too Late)

The guideline about not inheriting from collection classes isn't C# specific, you'll find it in many programming languages. It is received wisdom not a law. One reason is that in practice composition is considered to often win out over inheritance in terms of comprehensibility, implementability and maintainability. It's more common with real world / domain objects to find useful and consistent "hasa" relationships than useful and consistent "isa" relationships unless you're deep in the abstract, most especially as time passes and the precise data and behaviour of objects in code changes. This shouldn't cause you to always rule out inheriting from collection classes; but it may be suggestive.


क्या होगा यदि 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. क्या मेरे वर्ग के नाम का मतलब है कि वे क्या हैं?


यह composition बनाम inheritance का एक उत्कृष्ट उदाहरण है।

इस विशिष्ट मामले में:

क्या टीम जोड़े गए व्यवहार वाले खिलाड़ियों की एक सूची है

या

क्या टीम अपने आप का एक उद्देश्य है जो खिलाड़ियों की एक सूची होती है।

सूची विस्तार करके आप अपने आप को कई तरीकों से सीमित कर रहे हैं:

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

  2. क्या होता है यदि आप अन्य चीजों की सूचियां भी चाहते हैं। उदाहरण के लिए, टीमों के पास कोच, प्रबंधक, प्रशंसकों, उपकरण इत्यादि हैं। उनमें से कुछ अच्छी तरह से अपने अधिकार में सूचीबद्ध हो सकते हैं।

  3. आप विरासत के लिए अपने विकल्पों को सीमित करते हैं। उदाहरण के लिए आप एक सामान्य टीम ऑब्जेक्ट बनाना चाहते हैं, और उसके बाद बेसबॉल टीम, फुटबॉल टीम इत्यादि हो सकते हैं। सूची से प्राप्त करने के लिए आपको टीम से विरासत करने की आवश्यकता है, लेकिन इसका मतलब है कि सभी विभिन्न प्रकार की टीमों को उस रोस्टर के समान कार्यान्वयन के लिए मजबूर होना पड़ता है।

संरचना - आपके ऑब्जेक्ट के अंदर इच्छित व्यवहार देने वाली ऑब्जेक्ट सहित।

विरासत - आपकी वस्तु उस वस्तु का एक उदाहरण बन जाती है जिसमें आपके इच्छित व्यवहार होता है।

दोनों के पास उनके उपयोग हैं, लेकिन यह एक स्पष्ट मामला है जहां संरचना बेहतर है।


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

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

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

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 के बीच चोट के कारण कितने सेहॉक्स खिलाड़ी खेल चूक गए?" या "डेनवर खिलाड़ी जो पहले किसी अन्य टीम के लिए खेले थे, साल में सालाना सालाना बढ़ोतरी हुई थी?" या " क्या पिगर्स इस साल सभी तरह से जाते थे? "

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


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

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

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


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 
}

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.


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();
}

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.


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.


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.


There are a lot excellent answers here, but I want to touch on something I didn't see mentioned: Object oriented design is about empowering objects .

You want to encapsulate all your rules, additional work and internal details inside an appropriate object. In this way other objects interacting with this one don't have to worry about it all. In fact, you want to go a step further and actively prevent other objects from bypassing these internals.

When you inherit from List , all other objects can see you as a List. They have direct access to the methods for adding and removing players. And you'll have lost your control; उदाहरण के लिए:

Suppose you want to differentiate when a player leaves by knowing whether they retired, resigned or were fired. You could implement a RemovePlayer method that takes an appropriate input enum. However, by inheriting from List , you would be unable to prevent direct access to Remove , RemoveAll and even Clear . As a result, you've actually disempowered your FootballTeam class.

Additional thoughts on encapsulation... You raised the following concern:

It makes my code needlessly verbose. I must now call my_team.Players.Count instead of just my_team.Count.

You're correct, that would be needlessly verbose for all clients to use you team. However, that problem is very small in comparison to the fact that you've exposed List Players to all and sundry so they can fiddle with your team without your consent.

You go on to say:

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're wrong about the first bit: Drop the word 'list', and it's actually obvious that a team does have players.
However, you hit the nail on the head with the second. You don't want clients calling ateam.Players.Add(...) . You do want them calling ateam.AddPlayer(...) . And your implemention would (possibly amongst other things) call Players.Add(...) internally.

Hopefully you can see how important encapsulation is to the objective of empowering your objects. You want to allow each class to do its job well without fear of interference from other objects.


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.


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.


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.
}

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

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







design