隐含地投射集合的泛型

时间:2013-11-01 17:14:39

标签: c# generics unity3d

昨晚我了解了casting by example的这个精彩操作:使用对现有实例的引用生成某些Type集合的一种非常酷的方法。

我的问题是,虽然这在显式创建实例时有效,但如果使用激活器从类型实例化,则生成的集合类型不准确。

    class TestCollectionContent
    {
        public int id { get; private set; }
    }

    [Test]
    public void TestListCastCreation()
    {
        var explicitCast = new TestCollectionContent ();    //This casts as TestCollectionContent
        var explicitList = MakeList (explicitCast);         //This casts as List<CommandWithExecute>
        explicitList.Add (new TestCollectionContent ());

        Type clazz = typeof(TestCollectionContent);
        var implicitCast = Activator.CreateInstance (clazz);//This casts as TestCollectionContent
        var implicitList = MakeList (implicitCast);         //This casts as List<object>
        implicitList.Add (new TestCollectionContent ());

        Assert.AreEqual (explicitCast.GetType (), implicitCast.GetType ()); //Succeeds
        Assert.AreEqual (explicitList.GetType (), implicitList.GetType ()); //FAILS!
    }

    public static List<T> MakeList<T>(T itemOftype)
    {
        List<T> newList = new List<T>();
        return newList;
    } 

为了我的目的,必须正确地收集集合。有什么想法吗?

请注意,我正在使用带有Unity3D的C#(它使用的东西类似于.Net 3.5)。

2 个答案:

答案 0 :(得分:1)

Activator.CreateInstance始终返回object,因此在使用时不会从中获取任何静态类型信息。这将使implicitCast类型的变量object,尽管它的值是更专业的类型。

现在使用泛型时,只考虑可用于静态类型的类型。因此,在将implicitCast传递给MakeList时,所有方法都会看到object。因此,该方法将被称为MakeList<object>,并将返回List<object>,其当然与explicitList的类型不同。

不幸的是(或幸运的是?)你真的不能做得更好。泛型应该是在静态类型环境中使用的东西,如果你开始动态创建类型,你将失去这种能力。

然而,您可以通过执行以下操作来使用Activator.CreateInstance创建列表:

public static IList MakeList(object itemOftype)
{
    Type listType = typeof(List<>).MakeGenericType(itemOfType.GetType());
    return (IList) Activator.CreateInstance(listType);
}

当然,这也只会返回一个对象,因此您必须将其转换为更专业的类型,或者使用非通用IList接口至少具有一些访问权限。

答案 1 :(得分:0)

此代码的行为方式是因为T在编译时是inferred,而不是运行时。由于implicitCast的类型为object,因此会使用MakeList<object>进行编译。

var implicitList = MakeList (implicitCast); // equivalent to
List<object> implicitList = MakeList<object>(implicitCast);

var explicitList = MakeList (explicitCast); // equivalent to
List<TestCollectionContent> explicitList =
                   MakeList<TestCollectionContent>(explicitCast);

如果您希望它使用运行时类型,您可以使用反射或dynamic