1

This code:

public interface IInter { }
public class Concrete : IInter { /*... body ...*/ }
var t = (List<IInter>)new List<Concrete>();

Yields this error:

Cannot convert type 'System.Collections.Generic.List<Concrete>' to 'System.Collections.Generic.List<IInter>'

Why is it ? how do I overcome it ? my goal is this:

var t = new List<List<IInter>>()
{
    new List<ConcreteA>(){/* ... data ... */},
    new List<ConcreteB>(){/* ... data ... */},
    // ...
    new List<ConcreteX>(){/* ... data ... */},
};

Edit:

Thanks for all your help. Ahh, I kinda abstracted things, to make it easier to read... but my real problem is this:

public class SingletonFactory<T> where T : IToken
{
    private SingletonFactory() { }
    private static SingletonFactory<T> _instance = new SingletonFactory<T>();
    public static SingletonFactory<T> Instance { get { return _instance; } }

    public T Produce(int position) { return (T)Activator.CreateInstance(typeof(T), position); }
    public T Produce(int position, string token) { return (T)Activator.CreateInstance(typeof(T), position, token); }
}

And then:

var keywords = new Dictionary<string, SingletonFactory<IToken>>()
{
    { "abc", SingletonFactory<Abc>.Instance },
    { "xyz", SingletonFactory<Xyz>.Instance },
    { "123", SingletonFactory<Num>.Instance }
};

So I guess it's much more complicated...

I'm using c# 4.0

6 Answers 6

2
var res = new List<Concrete>().Cast<IInter>().ToList();
Sign up to request clarification or add additional context in comments.

Comments

2

A List<Concrete> is not a List<IInter> although Concrete implements IInter.

You can use:

List<IInter> t = ConcreteList.Cast<IInter>().ToList();

Covariance and Contravariance FAQ

Comments

2

Because you cannot assign a List<ConcreteA> to a List<IInter>, otherwise you could do this:

concreteAList = newList<ConcreteA>();
List<Inter> interList = concreteAList as List<Inter>;  // seems harmless
interList.Add(new ConcreteB());                        // not allowable

You could do:

var t = new List<List<IInter>>()
{
    new List<IInter>(){/* ... fill with ConcreteAs ... */},
    new List<IInter>(){/* ... fill with ConcreteBs ... */},
    // ...
    new List<IInter>(){/* ... fill with ConcreteXs ... */},
};

But I don't know if that accomplishes what you want.

Comments

2

This is called covariance and C# supports covariance (and contravariance) on generic interfaces and not generic classes. This is the following works but your example does not:

IEnumerable<IInter> e = new List<Concrete>();
ICollection<IInter> c = new List<Concrete>();

Covariance is also supported on arrays:

IInter[] a = new Concrete[3];

6 Comments

Are you sure that we can write the first line? I did not know that and could not make it work. May it be new List<IInter> instead of new List<Concrete>
@MehmetAtaş: actually I was right the first time (damn you self doubt). Covariance for generic interfaces was added in .NET 4, so perhaps you are using an earlier version?
I did a little bit of tests and now I am clear. It works for FW >= 4.0 and does not work for FW < 4.0 (:
I use c# 4.0, is there a way to do it with Dictionary<> too (see my edit) ?
@Tal, IDictionary<IFruit> fruits = new Dictionary<Banana>();
|
1

Make a covrariant interface for the methods you need to access from SingletonFactory<T>. This will ensure that your type T is output type safe.

public interface IToken { }
public class Abc : IToken { }
public class Xyz : IToken { }
public class Num : IToken { }

public interface ISingletonFactory<out T> where T : IToken
{
    T Produce(int position);
    T Produce(int position, string token);
}

public class SingletonFactory<T> : ISingletonFactory<T> where T : IToken
{
    private SingletonFactory() { }
    private static SingletonFactory<T> _instance = new SingletonFactory<T>();
    public static SingletonFactory<T> Instance { get { return _instance; } }

    public T Produce(int position) { return (T)Activator.CreateInstance(typeof(T), position); }
    public T Produce(int position, string token) { return (T)Activator.CreateInstance(typeof(T), position, token); }
}

var keywords = new Dictionary<string, ISingletonFactory<IToken>>()
{
    { "abc", SingletonFactory<Abc>.Instance },
    { "xyz", SingletonFactory<Xyz>.Instance },
    { "123", SingletonFactory<Num>.Instance }
};

Comments

1

One way is:

IEnumerable<IInter> t = new List<Concrete>();

Another is:

List<IInter> t = new List<Concrete>().ConvertAll(x => (IInter) x);

Edit, adding better sample to show that it works (the test passes):

[Test]
public void CovarianceTest()
{
    var concrete = new Concrete();
    IEnumerable<IInter> t = new List<Concrete> { concrete };
    Assert.IsTrue(new[] { concrete }.SequenceEqual(t));

    List<IInter> t2 = new List<Concrete> { concrete }.ConvertAll(x => (IInter)x);
    Assert.IsTrue(new[] { concrete }.SequenceEqual(t2));
}

4 Comments

Are you sure that we can write the first line? I did not know that and could not make it work. May it be new List<IInter> instead of new List<Concrete>
@MehmetAtaş yes because IEnumerable<T> is covariant (you couldn't add a different type to the underlying list)
Ok, I tested too and now I am clear. It works for FW >= 4.0 and does not work for FW < 4.0 (:
The first method is great, but my real problem (sorry for not mentioning it first place - see my edit) is with Dictionary<>. The second would change my object's behavior.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.