166

The code looks like below:

namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}

When I Run Code Analysis i get the following recommendation.

Warning 3 CA1002 : Microsoft.Design : Change 'List' in 'IMyClass.GetList()' to use Collection, ReadOnlyCollection or KeyedCollection

How should I fix this and what is good practice here?

abatishchev
  • 92,232
  • 78
  • 284
  • 421
bovium
  • 2,699
  • 6
  • 23
  • 35

8 Answers8

243

To answer the "why" part of the question as to why not List<T>, The reasons are future-proofing and API simplicity.

Future-proofing

List<T> is not designed to be easily extensible by subclassing it; it is designed to be fast for internal implementations. You'll notice the methods on it are not virtual and so cannot be overridden, and there are no hooks into its Add/Insert/Remove operations.

This means that if you need to alter the behavior of the collection in the future (e.g. to reject null objects that people try to add, or to perform additional work when this happens such as updating your class state) then you need to change the type of collection you return to one you can subclass, which will be a breaking interface change (of course changing the semantics of things like not allowing null may also be an interface change, but things like updating your internal class state would not be).

So by returning either a class that can be easily subclassed such as Collection<T> or an interface such as IList<T>, ICollection<T> or IEnumerable<T> you can change your internal implementation to be a different collection type to meet your needs, without breaking the code of consumers because it can still be returned as the type they are expecting.

API Simplicity

List<T> contains a lot of useful operations such as BinarySearch, Sort and so on. However if this is a collection you are exposing then it is likely that you control the semantics of the list, and not the consumers. So while your class internally may need these operations it is very unlikely that consumers of your class would want to (or even should) call them.

As such, by offering a simpler collection class or interface, you reduce the number of members that users of your API see, and make it easier for them to use.

Bakudan
  • 17,636
  • 9
  • 48
  • 69
Greg Beech
  • 122,952
  • 42
  • 199
  • 241
  • 1
    This response is dead on. Another good read on the subject can be found here: http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt-instead-of-list-lt-t-gt-david-kean.aspx – senfo Nov 07 '08 at 15:58
  • 7
    I see your first points, but I don't know if I agree on your API simplicity part. – Boris Callens Mar 05 '09 at 10:22
  • http://stackoverflow.com/a/398988/2632991 Here is a real good post too, about what is the difference between Collections and Lists. – El Mac Nov 24 '16 at 08:34
  • 2
    http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt-instead-of-list-lt-t-gt-david-kean.aspx -> Is dead. Do we have a newer information for this ? – Machado Feb 22 '17 at 19:24
  • 1
    Working link: https://blogs.msdn.microsoft.com/kcwalina/2005/09/26/why-we-dont-recommend-using-listt-in-public-apis/ – ofthelit Nov 26 '18 at 08:46
  • I see below that Skeet himself advocates interfaces (at time of writing at least). But ive seen a LOT of hardcore proponents that return types should be as concrete and specific as possible. Is your stance also that interfaces are only for when you really see a concrete situation where you might return different list types for example. Or do you feel there is reason to default to interface usage? – Base Aug 26 '19 at 06:13
50

I would personally declare it to return an interface rather than a concrete collection. If you really want list access, use IList<T>. Otherwise, consider ICollection<T> and IEnumerable<T>.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • 1
    Would the IList then extend the ICollection interface? – bovium Nov 07 '08 at 10:31
  • 1
    Yes, IList extends ICollection. I'll add documentation links. – Jon Skeet Nov 07 '08 at 10:32
  • 8
    @Jon: I know this is old, but can you comment on what Krzysztof says at http://blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx? Specifically his comment, `We recommend using Collection, ReadOnlyCollection, or KeyedCollection for outputs and properties and interfaces IEnumerable, ICollection, IList for inputs.` CA1002 seems to go along with Krzysztof's comments. I can't imagine why a concrete collection would be recommended instead of an interface, and why the distinction between inputs/outputs. – Nelson Rothermel Oct 05 '10 at 15:46
  • 3
    @Nelson: It's rare that you want to *require* callers to pass in an immutable list, but it's reasonable to *return* one so that they know it's definitely immutable. Not sure about the other collections though. Would be nice to have more detail. – Jon Skeet Oct 05 '10 at 17:07
  • 2
    It's not for a specific case. Obviously in general `ReadOnlyCollection` wouldn't make sense for an input. Similarly, `IList` as an input says, "I need Sort() or some other member that an IList has" which doesn't make sense for an output. But what I meant was, why would `ICollection` be recommended as an input and `Collection` as an output. Why *not* use `ICollection` as the output also as you suggested? – Nelson Rothermel Oct 05 '10 at 17:28
  • 1
    @Nelson: I don't know, to be honest. It would be good to get more information from Krzysztof. – Jon Skeet Oct 05 '10 at 17:48
  • 9
    I'm thinking it has to do with inambiguity. `Collection` and `ReadOnlyCollection` both derive from `ICollection` (i.e. there is no `IReadOnlyCollection`). If you return the interface, it's not obvious which one it is and whether it can be modified. Anyway, thanks for your input. This was a good sanity check for me. – Nelson Rothermel Oct 05 '10 at 17:54
4

I don't think anyone has answered the "why" part yet... so here goes. The reason "why" you "should" use a Collection<T> instead of a List<T> is because if you expose a List<T>, then anyone who gets access to your object can modify the items in the list. Whereas Collection<T> is supposed to indicate that you are making your own "Add", "Remove", etc methods.

You likely don't need to worry about it, because you're probably coding the interface for yourself only (or maybe a few collegues). Here's another example that might make sense.

If you have a public array, ex:

public int[] MyIntegers { get; }

You would think that because there is only a "get" accessor that no-one can mess with the values, but that's not true. Anyone can change the values inside there just like this:

someObject.MyIngegers[3] = 12345;

Personally, I would just use List<T> in most cases. But if you are designing a class library that you are going to give out to random developers, and you need to rely on the state of the objects... then you'll want to make your own Collection and lock it down from there :)

Noctis
  • 10,865
  • 3
  • 38
  • 74
Timothy Khouri
  • 29,873
  • 18
  • 80
  • 125
  • "If you return List to the client code, you will not ever be able to receive notifications when client code modifies the collection." - FX COP... See also "http://msdn.microsoft.com/en-us/library/0fss9skc.aspx"... wow, seems I'm not off base after all :) – Timothy Khouri Nov 08 '08 at 02:54
  • Wow - years later I see that the link I pasted in the above comment had a quote at the end of it, so it didn't work... http://msdn.microsoft.com/en-us/library/0fss9skc.aspx – Timothy Khouri Oct 17 '11 at 22:12
2

It's mostly about abstracting your own implementations away instead of exposing the List object to be manipulated directly.

It's not good practice to let other objects (or people) modify the state of your objects directly. Think property getters/setters.

Collection -> For normal collection
ReadOnlyCollection -> For collections that shouldn't be modified
KeyedCollection -> When you want dictionaries instead.

How to fix it depends on what you want your class to do and the purpose of the GetList() method. Can you elaborate?

chakrit
  • 57,172
  • 24
  • 125
  • 160
  • 1
    But Collection and KeyedCollection are not readonly either. – nawfal Jul 09 '14 at 07:40
  • @nawfal And where did I say that it is? – chakrit Jul 09 '14 at 12:02
  • 1
    You state *not let other objects (or people) modify the state of your objects directly* and list 3 collection types. Barring `ReadOnlyCollection` the other two doesn't obey it. – nawfal Jul 09 '14 at 12:08
  • That's referring to "good practice". Proper context please. That list below simply state the basic requirement of such types since the OP wants to understand the warning. I then go on to ask him about the purpose of `GetList()` as to be able to help him more correctly. – chakrit Jul 09 '14 at 12:10
  • In no way did I state that using the 3 types will immediately gets you to a "good practice" position. – chakrit Jul 09 '14 at 12:11
  • 1
    Ok fine I understand that part. But still the reasoning part is not sound according to me. You say `Collection` helps in abstracting internal implementation and prevents direct manipulation of internal list. How? `Collection` is merely a wrapper, operating on the same instance passed. It's a dynamic collection meant for inheritance, nothing else (Greg's answer is more relevant here). – nawfal Jul 09 '14 at 12:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57020/discussion-between-nawfal-and-chakrit). – nawfal Jul 09 '14 at 12:20
1

In these kind of case I usually try to expose the least amount of implemententation that is needed. If the consumers do not need to know that you are actually using a list then you don't need to return a list. By returning as Microsoft suggests a Collection you hide the fact that you are using a list from the consumers of your class and isolate them against an internal change.

Harald Scheirich
  • 9,346
  • 25
  • 51
1

Something to add though it's been a long time since this was asked.

When your list type derives from List<T> instead of Collection<T>, you cannot implement the protected virtual methods that Collection<T> implements. What this means is that you derived type cannot respond in case any modifications are made to the list. This is because List<T> assumes you are aware when you add or remove items. Being able to response to notifications is an overhead and hence List<T> doesn't offer it.

In cases when external code has access to your collection, you may not be in control of when an item is being added or removed. Therefore Collection<T> provides a way to know when your list was modified.

NullReference
  • 2,448
  • 2
  • 27
  • 31
0

I don't see any problem with returning something like

this.InternalData.Filter(crteria).ToList();

If I returned a disconnected copy of internal data, or detached result of a data query - I can safely return List<TItem> without exposing any of implementation details, and allow to use the returned data in the convenient way.

But this depends on what type of consumer I expect - if this is a something like data grid I prefer to return IEnumerable<TItem> which will be the copied list of items anyway in most cases :)

nawfal
  • 62,042
  • 48
  • 302
  • 339
Konstantin Isaev
  • 564
  • 7
  • 13
-1

Well the Collection class is really just a wrapper class around other collections to hide their implementation details and other features. I reckon this has something to do with the property hiding coding pattern in object-oriented languages.

I think you shouldn't worry about it, but if you really want to please the code analysis tool, just do the following:

//using System.Collections.ObjectModel;

Collection<MyClass> myCollection = new Collection<MyClass>(myList);
Tamas Czinege
  • 110,351
  • 39
  • 146
  • 173
  • 1
    Sorry, that was a typo. I menat Collection. I am really looking forward to get my hands on C# 4 and generic covariance btw! – Tamas Czinege Nov 07 '08 at 11:14
  • Wrapping in a `Collection` protects neither itself nor the underlying collection as far as I understand. – nawfal Jul 09 '14 at 08:15
  • That piece of code was to "please the code analysis tool". I don't think @TamasCzinege said anywhere that using `Collection` will immediately protect your underlying collection. – chakrit Jul 09 '14 at 12:12