We're not supposed to use List<T> ? ( List )

I installed FxCop and had it analyze my application. In several spots, it says:

"Do not expose List<T> in object models. Use Collection<T>,
ReadOnlyCollection<T> or KeyedCollection<K,V> instead.
List<T> is meant to be used from implementation, not
in object model API. List<T> is optimized for performance
at the cost of long term versioning. For example, if
you return List<T> to the client code, you will not
ever be able to receive notifications when client code
modifies the collection."

I'm a bit weak in my OO.. so I'm not sure what "expose" really means in this context. Is this saying to not use List<T> Why not


Answer this question

We're not supposed to use List<T> ? ( List )

  • SameerNSameer

    I see..that makes sense. But really, if I'm just a solo developer writing software and I'll never release the source code.. it doesn't matter if I use List or Collection, right (since I'm the only person looking at the code)

  • mtronix

    Note, though, if you hide members as you have done, instead of overriding virtual members, the behavior changes depending on the compile-time type. For example, if you do this:

    List< Column > var = aColumnList;

    var.Add( "this" );

    It will call List<>.Add(), not ColumnList.Add(). You may say you'll never cast ColumnList as List< Column >, but a month or two down the road, you may have forgotten this bit, and do so because you came up with a situation where it's desirable to treat it as List<> seeing that List<> is a superclass.


  • Tim Dallmann

    Hi,

    I've implemented this thing in a treelist control I made recently.
    If you expose List<T> in your class, the user of the class can directly Add or Remove items from it using collection methods. Now what if you want to do some other things before adding it to the collection. for that we need a collection class.

    class ColumnList : List<Column>
    {
    // provide your own Add method
    public new void Add(string columnname)
    {
    Column col = new Column(columnname);
    col.Width = DEFAULT_WIDTH;
    base.Add(col);
    }
    }

    So, I'll expose ColumnList as a property in my class.

    regards


  • Kevin Rodgers

    A related, maybe more important, reason to use interface, rather than class, to expose features is that it faciliates looser coupling between your code and your users' code (or between different modules in your case).

    Anyway, there are general OO rules, but no dogma.  Each design decision has its own cost and benefit depending on the context, and you should use what you think is the best for the situation.


  • ernisj

    OK. So you're saying if the property Employees returns type List<Employee>, a programmer could somehow screw things up But if it returns type ICollection<Employee>, they can't How does that work

    Also, if I did want the ability to sort the list, then the Employees property would have to return type List<Employee>, right

  • Danny Thorpe MSFT

    Well,

    I don;t think anyone would ever do that. Suppose I am using a TreeList and I need to access a column in treelist, I do something like
    treeList.ColumnList[index].Add(). If someone is explicitly taking a List<Column> var, he has other things in mind.

    regards


  • Tryin2Bgood

    MDesigner wrote:
    Hm, I guess I don't fully understand. Whats wrong with being tied to the List<T> implementation If I have something like this:

    public class Roster
    {
    private string name;
    private List<Employee> listOfEmployees;

    public string Name
    {
    get { return name; }
    set { name = value; }
    }

    public List<Employee> EmployeeList
    {
    get { return listOfEmployees; }
    }
    }


    ...what's wrong with using List<T> this way
    It's no better than simply making listOfEmployees public. If you just want to let users iterate a collection of items (i.e. the fact that it's a list and they're unsorted and indexed doesn't matter) then returning ICollection<T> will do the same thing. Of course, naming the property EmployeeList sort of suggest that it should be a list :-).

    public ICollection<Employee> Employees
    {
    get { return listOfEmployees; }
    }


    ...would do the same thing and you wouldn't be exposing implementation details.

    Also, it's generally a bad idea to return a mutable collection through the interface. This lets clients to your class modify internal data, unchecked. I'm surprised FxCop didn't want about that as well.



  • iliketoeaticecream

    AbhimanyuSirohi wrote:

    Well,

    I don;t think anyone would ever do that. Suppose I am using a TreeList and I need to access a column in treelist, I do something like
    treeList.ColumnList[index].Add(). If someone is explicitly taking a List<Column> var, he has other things in mind.

    regards

    AbhimanyuSirohi, treeList.ColumnList[index].Add() is simply shorthand for:

    ColumnList columnList = treeList.ColumnList;

    Column column = columnList[index];

    column.Add();



  • bobslayer

    Hm, I guess I don't fully understand. Whats wrong with being tied to the List<T> implementation If I have something like this:

    public class Roster
    {
       private string name;
       private List<Employee> listOfEmployees;

       public string Name
       {
          get { return name; }
          set { name = value; }
       }

       public List<Employee> EmployeeList
       {
          get { return listOfEmployees; }
       }
    }


    ...what's wrong with using List<T> this way

  • RossDempster

    They're suggesting you use a more generic (not Generics) type. For example, a List is a specific type of collection. If there's a more generic type that a type implements, it's better to use that while still using the specialized type for the implementation.

    For example, if you have a member of type List<T> in a class (say "Car") and you want to expose it (say in a property called "Tires"), you'd set the type of that property to the most generic interface/type that List<T> implements, like ICollection.

    That way, you further abstract your implementation from your interface. If you return List<T> as the type, then you're tied to that implementation. If you return ICollection you can switch your implementation from List<T> to Stack<T> without changing your outward-facing interface.



  • begginer_gameprogrammer

    Member hiding breaks polymorphism.  If the class is a utility class or part of model module (as in MVC), the chance of misuse would be greater.  But I agree it's less likely in your context (i.e., purpose-built GUI control).

    This is why I wish C# makes members virtual by default - the benefit would outweigh the performance hit.   Ok, I went too far off-topic.

    Cheers. 

     


  • HMCSharon

    List<> exposes many more members (features) than ICollection<>, so unless those additional exposed members of List<> are actually necessary, it's safer to expose more restricted interface of ICollection<>. If later, you found that you need some features not provided by ICollection<>, you can expose/add them to the class by exposing more featureful interface (e.g., IList<>) or adding methods to the same effect. On the other hand, if you exposed List<>, but later found that List<> exposes some features that you actually don't want users to use, it's gets hairy to prevent their use since users may already be using those features. Basically, expose as little as necessary - exposing more later is always easier than trying to take back what has already been exposed.

    Interfaces, like ICollection, IList, and so on, are in general designed for exposure - hence the name interface. ;-) So it'd be better to use them to expose members/features than implementation classes like List<>.

    On the other hand, if you feel most/all List<> features are necessary features to be exposed, then exposing List<> would be expedient.


  • Boris Zakharin

    MDesigner wrote:
    OK. So you're saying if the property Employees returns type List<Employee>, a programmer could somehow screw things up But if it returns type ICollection<Employee>, they can't How does that work

    Also, if I did want the ability to sort the list, then the Employees property would have to return type List<Employee>, right
    Yes, it's a separate issue. Normally, you'd pass back a reference to a copy of the collection rather than a reference to the internal implementation:

    public ICollection<Employee> Employees
    {
    get { return new List<Employee>(listOfEmployees); }
    }



  • nec4b

    MDesigner wrote:
    I see..that makes sense. But really, if I'm just a solo developer writing software and I'll never release the source code.. it doesn't matter if I use List or Collection, right (since I'm the only person looking at the code)
    Right, most errors/warnings/informationals from FxCop have no bearing on functionality (i.e. they're non-breaking). I'm assuming because you're using FxCop you're looking to write "better" code ("better" meaning easier to maintain, uses patterns that have proven to increase robustness and reliability when evolved, etc.). Of course, there's many ways to write code to provide certain functionality--if that's your only criteria, you're all set, you can ignore most of the messages that FxCop spits out (which you can set to ignore if you want...).

    FxCop messages (at least the majority) are based on proven techniques for writing code that is more robust, multithreading capable, reliable, easier to maintain, etc. Yes, you're a one-man-shop and you're the only guy looking at the code... I'm generally a one-man-shop, and take it from me, it's easier to fix an FxCop warning early in the life-cycle than it is to ignore it only to have to completely re-architect the program later because you went down a path that can't do that you want to do. Multithreading is a perfect example. You can have a lovely class with all sorts of goodies in it; but, if you're passing back a reference to a private variable you have no clean way of synchronizing that, each class that uses that return value ends up having to synchronize to some pseudo-global variable...

    It's not so much an issue with larger shops where *much* time is spent on designing these interfaces. But, with smaller shops where the implementation tends to evolve greatly over time, it's easy to paint yourself into a corner.



  • We're not supposed to use List<T> ? ( List )