Why not inherit from List?

前端 未结 27 1759
悲哀的现实
悲哀的现实 2020-11-21 05:39

When planning out my programs, I often start with a chain of thought like so:

A football team is just a list of football players. Therefore, I should

相关标签:
27条回答
  • 2020-11-21 06:19

    Prefer Interfaces over Classes

    Classes should avoid deriving from classes and instead implement the minimal interfaces necessary.

    Inheritance breaks Encapsulation

    Deriving from classes breaks encapsulation:

    • exposes internal details about how your collection is implemented
    • declares an interface (set of public functions and properties) that may not be appropriate

    Among other things this makes it harder to refactor your code.

    Classes are an Implementation Detail

    Classes are an implementation detail that should be hidden from other parts of your code. In short a System.List is a specific implementation of an abstract data type, that may or may not be appropriate now and in the future.

    Conceptually the fact that the System.List data type is called "list" is a bit of a red-herring. A System.List<T> is a mutable ordered collection that supports amortized O(1) operations for adding, inserting, and removing elements, and O(1) operations for retrieving the number of elements or getting and setting element by index.

    The Smaller the Interface the more Flexible the Code

    When designing a data structure, the simpler the interface is, the more flexible the code is. Just look at how powerful LINQ is for a demonstration of this.

    How to Choose Interfaces

    When you think "list" you should start by saying to yourself, "I need to represent a collection of baseball players". So let's say you decide to model this with a class. What you should do first is decide what the minimal amount of interfaces that this class will need to expose.

    Some questions that can help guide this process:

    • Do I need to have the count? If not consider implementing IEnumerable<T>
    • Is this collection going to change after it has been initialized? If not consider IReadonlyList<T>.
    • Is it important that I can access items by index? Consider ICollection<T>
    • Is the order in which I add items to the collection important? Maybe it is an ISet<T>?
    • If you indeed want these thing then go ahead and implement IList<T>.

    This way you will not be coupling other parts of the code to implementation details of your baseball players collection and will be free to change how it is implemented as long as you respect the interface.

    By taking this approach you will find that code becomes easier to read, refactor, and reuse.

    Notes about Avoiding Boilerplate

    Implementing interfaces in a modern IDE should be easy. Right click and choose "Implement Interface". Then forward all of the implementations to a member class if you need to.

    That said, if you find you are writing lots of boilerplate, it is potentially because you are exposing more functions than you should be. It is the same reason you shouldn't inherit from a class.

    You can also design smaller interfaces that make sense for your application, and maybe just a couple of helper extension functions to map those interfaces to any others that you need. This is the approach I took in my own IArray interface for the LinqArray library.

    0 讨论(0)
  • 2020-11-21 06:20

    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.

    0 讨论(0)
  • 2020-11-21 06:21

    When is it acceptable?

    To quote Eric Lippert:

    When you're building a mechanism that extends the List<T> mechanism.

    For example, you are tired of the absence of the AddRange method in IList<T>:

    public interface IMoreConvenientListInterface<T> : IList<T>
    {
        void AddRange(IEnumerable<T> collection);
    }
    
    public class MoreConvenientList<T> : List<T>, IMoreConvenientListInterface<T> { }
    
    0 讨论(0)
提交回复
热议问题