如何基于泛型类型创建通用实例

时间:2017-09-07 08:14:10

标签: c# generics

我有两个存储空间。第一个很简单。第二个向实体(meta)添加附加信息。我想编写基于我的实体的泛型类型创建存储的工厂。但我无法做到。我收到了编译错误。也许我想要一些奇怪的东西,我应该重写架构。我也尝试使用反射,但它也没有用。这是一个例子:

using System;
using System.Reflection;

namespace ConsoleApp5
{
    interface IStorage<T>
    {
        void Save();
    }

    class Storage<T> : IStorage<T>
    {
        public virtual void Save()
        {
            Console.WriteLine("Save");
        }
    }

    class StorageWithMeta<T> : Storage<T> where T : EntityWithMeta
    {
        public override void Save()
        {
            Console.WriteLine("Save With Meta");
        }
    }

    abstract class EntityWithMeta
    {
    }

    class StorageFactory
    {
        public static IStorage<T> Create<T>()
        {
            if (typeof(EntityWithMeta).IsAssignableFrom(typeof(T)))
            {
                return CreateWithMeta<T>(); //compilation error! (type T must be convertible to EntityWithMeta)

                //reflection based approach:
                //var methodInfo = typeof(StorageFactory).GetMethod("CreateWithMeta", BindingFlags.Static | BindingFlags.NonPublic);
                //return (IStorage<T>) methodInfo.Invoke(null, null); //System.InvalidOperationException. ContainsGenericParameters is true
            }
            return new Storage<T>();
        }

        private static IStorage<T> CreateWithMeta<T>() where T : EntityWithMeta
        {
            return new StorageWithMeta<T>();
        }
    }

    class MyClass1
    {
    }

    class MyClass2 : EntityWithMeta
    {
    }

    class EntryPoint
    {
        public static void Main()
        {
            StorageFactory.Create<MyClass1>().Save();//expected Save
            StorageFactory.Create<MyClass2>().Save();//expected Save With Meta
        }
    }
}

1 个答案:

答案 0 :(得分:2)

问题在于,尽管运行时检查,编译器仍无法保证T可分配给EntityWithMeta。换句话说,它不知道if语句在上下文中的含义。您可以使用Activator.CreateInstanceType.MakeGenericType

解决此问题
return (IStorage<T>)Activator.CreateInstance(typeof(StorageWithMeta<>).MakeGenericType(typeof(T)));

关于基于反射的方法的注释(您在OP中注释掉了):方法CreateWithMeta本身就是通用的。您拥有的MethodInfo对象用于通用方法定义。您处于正确的轨道,但您必须使用MethodInfo.MakeGenericMethod

创建构造的通用方法
var methodInfo = typeof(StorageFactory).GetMethod("CreateWithMeta", BindingFlags.Static | BindingFlags.NonPublic);
var constructedGeneric = methodInfo.MakeGenericMethod(typeof(T));
return (IStorage<T>)constructedGeneric.Invoke(null, null);