Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I not return a List<Foo> if asked for a List<IFoo>? [duplicate]

I understand that, if S is a child class of T, then a List<S> is not a child of List<T>. Fine. But interfaces have a different paradigm: if Foo implements IFoo, then why is a List<Foo> not (an example of) a List<IFoo>?

As there can be no actual class IFoo, does this mean that I would always have to cast each element of the list when exposing a List<IFoo>? Or is this simply bad design and I have to define my own collection class ListOfIFoos to be able to work with them? Neither seem reasonable to me...

What would be the best way of exposing such a list, given that I am trying to program to interfaces? I am currently tending towards actually storing my List<Foo> internally as a List<IFoo>.

like image 548
Joel in Gö Avatar asked Nov 25 '08 13:11

Joel in Gö


4 Answers

Your List<Foo> is not a subclass if List<IFoo> because you cannot store an MyOwnFoo object in it, which also happens to be an IFoo implementation. (Liskov substitution principle)

The idea of storing a List<IFoo> instead of a dedicated List<Foo> is OK. If you need casting the list's contents to it's implementation type, this probably means your interface is not appropriate.

like image 174
xtofl Avatar answered Nov 12 '22 07:11

xtofl


Here's an example of why you can't do it:

// Suppose we could do this...
public List<IDisposable> GetDisposables()
{
    return new List<MemoryStream>();
}

// Then we could do this
List<IDisposable> disposables = GetDisposables();
disposables.Add(new Form());

At that point a list which was created to hold MemoryStreams now has a Form in it. Bad!

So basically, this restriction is present to maintain type safety. In C# 4 and .NET 4.0 there will be limited support for this (it's called variance) but it still won't support this particular scenario, for exactly the reasons given above.

like image 25
Jon Skeet Avatar answered Nov 12 '22 09:11

Jon Skeet


In your returning function, you have to make the list a list of interfaces, and when you create the object, make it as an object that implements it. Like this:

function List<IFoo> getList()
{
  List<IFoo> r = new List<IFoo>();
  for(int i=0;i<100;i++)
      r.Add(new Foo(i+15));

  return r;
}
like image 32
Tom Ritter Avatar answered Nov 12 '22 08:11

Tom Ritter


MASSIVE EDIT You'll be able to do it with C# 4.0, but [thanks Jon]

You can get around it using ConvertAll:

public List<IFoo> IFoos()
{
    var x = new List<Foo>(); //Foo implements IFoo
    /* .. */
    return x.ConvertAll<IFoo>(f => f); //thanks Marc
}
like image 5
3 revs, 2 users 89% Avatar answered Nov 12 '22 09:11

3 revs, 2 users 89%