views:

193

answers:

3

Possible Duplicate:
Upcasting and generic lists

Ok, I want to send a List<CardHolder> as an IEnumerable<ICardHolder> where CardHolder : ICardHolder. However, the compiler errors:

Error 4 Argument '1': cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable'

This seems strange to me, considering that an List<T> : IEnumerable<T>. What's going wrong?

public interface ICardHolder
{
    List<Card> Cards { get; set; } 
}

public class CardHolder : ICardHolder
{
    private List<Card> cards = new List<Card>();
    public List<Card> Cards
    {
        get { return cards; }
        set { cards = value; }
    }

    // ........
}

public class Deck : ICardHolder
{
    // .........

    public void Deal(IEnumerable<ICardHolder> cardHolders)
    {
         // ........
    }

    // .........
}

public class Game
{
    Deck deck = new Deck();
    List<CardHolder> players = new List<CardHolder>();

    // .........

    deck.Deal(players); // Problem is here!

    // .........
}
+6  A: 

The problem is that List<T> is not a subtype of IEnumerable<T1> even if T : T1.

Generics in C# (before C# 4.0) are 'invariant' (ie. don't have this sub-typing relationship). In .Net 4, IEnumerable<T> will have its type parameter annotated as being 'covariant'. This means that List<T> will be a subtype of IEnumerable<T1> if T : T1.

See this page on MSDN for more details of this feature.

Edit - You can work around this in your case by making the Deal method generic:

public void Deal<T>(IEnumerable<T> cardHolders) where T : ICardHolder
{
     // ........
}
Ben Lings
OK, so I need to convert my List<CardHolder> to List<ICardHolder> to be able to send it as an IEnumerable<ICardHolder>? Oh, and is it possible to get some sort of preview for C# 4.0 on VS2008?
Callum Rogers
There an excellent InfoQ article on co/contravariance in Generics here: http://www.infoq.com/news/2008/08/GenericVariance
joshua.ewer
+50 for generic method (if I could). Thank you!
Callum Rogers
A: 

Adding to Ben's answer, you should replace the line:

    List<CardHolder> players = new List<CardHolder>();

with:

    List<ICardHolder> players = new List<ICardHolder>();

You're better off with interfaces anyway... :)

Aviad Ben Dov
Unfortunately, this doesn't work with the rest of the code I have, as it need to access some specific properties of CardHolder that ICardHolder doesn't (and cannot) have. But thanks anyway.
Callum Rogers
for CardHolder specific properties, you can always cast to CardHolder...
Thomas Levesque
+1  A: 

A List<CardHolder> is a IEnumerable<CardHolder>, but a IEnumerable<CardHolder> is not a IEnumerable<ICardHolder>. The two interfaces are unrelated (except for their structure).

C# 4.0 introduces covariance and contravariance, which can solve this kind of problem

Meanwhile, In C# 3.0, you can do :

deck.Deal(players.Cast<ICardHolder>());

It should have no significant performance impact, since the collection is only enumerated once, and the upcast to ICardHolder is a no-op in MSIL

Thomas Levesque