Why not inherit from List?

前端 未结 27 1711
悲哀的现实
悲哀的现实 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:04

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

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

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

    Because List 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 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 {
         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 and fix up any compile errors:

     class FootballTeam : IList {
         private List 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.

提交回复
热议问题