如何将嵌套泛型转换为从第一个继承的另一个嵌套泛型?

时间:2013-01-17 15:20:49

标签: c# generics

此代码:

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

产生此错误:

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

为什么?我该如何克服它?我的目标是:

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

编辑:

感谢您的帮助。啊,我有点抽象的东西,让它更容易阅读......但我真正的问题是:

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); }
}

然后:

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

所以我觉得它要复杂得多......

我正在使用c#4.0

6 个答案:

答案 0 :(得分:2)

var res = new List<Concrete>().Cast<IInter>().ToList();

答案 1 :(得分:2)

虽然List<Concrete>实施List<IInter>,但Concrete 不是IInter

您可以使用:

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

Covariance and Contravariance FAQ

答案 2 :(得分:2)

这称为 covariance ,C#支持通用接口而非泛型类的协方差(和逆变)。这是以下工作,但您的示例不是:

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

阵列也支持协方差:

IInter[] a = new Concrete[3];

答案 3 :(得分:2)

因为您无法将List<ConcreteA>分配给List<IInter>,否则您可以这样做:

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

可以做:

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

但我不知道这是否能达到你想要的效果。

答案 4 :(得分:1)

一种方法是:

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

另一个是:

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

编辑,添加更好的样本以显示它有效(测试通过):

[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));
}

答案 5 :(得分:1)

为您需要从SingletonFactory<T>访问的方法创建一个协作界面。这将确保您的类型T输出类型安全。

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 }
};