Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is returning IList<T> worse than returning T[] or List<T>?

The answers to questions like this: List<T> or IList<T> always seem to agree that returning an interface is better than returning a concrete implementation of a collection. But I'm struggling with this. Instantiating an interface is impossible, so if your method is returning an interface, it's actually still returning a specific implementation. I was experimenting a bit with this by writing 2 small methods:

public static IList<int> ExposeArrayIList()
{
    return new[] { 1, 2, 3 };
}

public static IList<int> ExposeListIList()
{
    return new List<int> { 1, 2, 3 };
}

And use them in my test program:

static void Main(string[] args)
{
    IList<int> arrayIList = ExposeArrayIList();
    IList<int> listIList = ExposeListIList();

    //Will give a runtime error
    arrayIList.Add(10);
    //Runs perfectly
    listIList.Add(10);
}

In both cases when I try to add a new value, my compiler gives me no errors, but obviously the method which exposes my array as an IList<T> gives a runtime error when I try to add something to it. So people who don't know what's happening in my method, and have to add values to it, are forced to first copy my IList to a List to be able to add values without risking errors. Of course they can do a typecheck to see if they're dealing with a List or an Array, but if they don't do that, and they want to add items to the collection they have no other choice to copy the IList to a List, even if it already is a List. Should an array never be exposed as IList?

Another concern of mine is based upon the accepted answer of the linked question (emphasis mine):

If you are exposing your class through a library that others will use, you generally want to expose it via interfaces rather than concrete implementations. This will help if you decide to change the implementation of your class later to use a different concrete class. In that case the users of your library won't need to update their code since the interface doesn't change.

If you are just using it internally, you may not care so much, and using List may be ok.

Imagine someone actually used my IList<T> they got from my ExposeListIlist() method just like that to add/remove values. Everything works fine. But now like the answer suggests, because returning an interface is more flexible I return an array instead of a List (no problem on my side!), then they're in for a treat...

TLDR:

1) Exposing an interface causes unnecessary casts? Does that not matter?

2) Sometimes if users of the library don't use a cast, their code can break when you change your method, even though the method remains perfectly fine.

I am probably overthinking this, but I don't get the general consensus that returning an interface is to be preferred over returning an implementation.

like image 343
Alexander Derck Avatar asked Dec 17 '15 12:12

Alexander Derck


People also ask

Should I return IList or List?

Generally best practice is to accept parameters of the most generic type and to return the most specific. However, conventionally programmers tend to not want to tie themselves to the List implementation and normally return the IList interface.

Is IList faster than List?

Results. IList<T> uses 40 Bytes more than List<T> .

What is difference between IList and List in C#?

The main difference between List and IList in C# is that List is a class that represents a list of objects which can be accessed by index while IList is an interface that represents a collection of objects which can be accessed by index.

What is IList in C# with example?

In C# IList interface is an interface that belongs to the collection module where we can access each element by index. Or we can say that it is a collection of objects that are used to access each element individually with the help of an index. It is of both generic and non-generic types.


3 Answers

Maybe this is not directly answering your question, but in .NET 4.5+, I prefer to follow these rules when designing public or protected APIs:

  • do return IEnumerable<T>, if only enumeration is available;
  • do return IReadOnlyCollection<T> if both enumeration and items count are available;
  • do return IReadOnlyList<T>, if enumeration, items count and indexed access are available;
  • do return ICollection<T> if enumeration, items count and modification are available;
  • do return IList<T>, if enumeration, items count, indexed access and modification are available.

Last two options assume, that method must not return array as IList<T> implementation.

like image 151
Dennis Avatar answered Oct 16 '22 08:10

Dennis


No, because the consumer should know what exactly IList is:

IList is a descendant of the ICollection interface and is the base interface of all non-generic lists. IList implementations fall into three categories: read-only, fixed-size, and variable-size. A read-only IList cannot be modified. A fixed-size IList does not allow the addition or removal of elements, but it allows the modification of existing elements. A variable-size IList allows the addition, removal, and modification of elements.

You can check for IList.IsFixedSize and IList.IsReadOnly and do what you want with it.

I think IList is an example of a fat interface and it should have been split into multiple smaller interfaces and it also violates Liskov substitution principle when you return an array as an IList.

Read more if you want to make decision about returning interface


UPDATE

Digging more and I found that IList<T> does not implement IList and IsReadOnly is accessible through base interface ICollection<T> but there is no IsFixedSize for IList<T>. Read more about why generic IList<> does not inherit non-generic IList?

like image 31
Hamid Pourjam Avatar answered Oct 16 '22 07:10

Hamid Pourjam


As with all "interface versus implementation" question, you'll have to realise what exposing a public member means: it defines the public API of this class.

If you expose a List<T> as a member (field, property, method, ...), you tell the consumer of that member: the type obtained by accessing this method is a List<T>, or something derived of that.

Now if you expose an interface, you hide the "implementation detail" of your class using a concrete type. Of course you can't instantiate IList<T>, but you can use an Collection<T>, List<T>, derivations thereof or your own type implementing IList<T>.

The actual question is "Why does Array implement IList<T>", or "Why has the IList<T> interface so many members".

It also depends on what you want the consumers of that member to do. If you actually return an internal member through your Expose... member, you'll want to return a new List<T>(internalMember) anyway, as otherwise the consumer can try and cast them to IList<T> and modify your internal member through that.

If you just expect consumers to iterate the results, expose IEnumerable<T> or IReadOnlyCollection<T> instead.

like image 13
CodeCaster Avatar answered Oct 16 '22 07:10

CodeCaster