将类型转换为通用

时间:2015-12-23 09:34:11

标签: .net generics

我有一个简单的方法,我需要将一个Type变量传递给另一个期望泛型的第三方方法调用。如果我强烈键入类型,它可以正常工作,但不能将其作为参数传入。

根据我的阅读,看起来这是不可能的,特别是如果需要高性能的话。

这种理解是否正确?

这有效:

if($result->num_rows > 0)

这不是:

public IEntity Get(Type type, object objectID)
    {
        if(_store == null) InitDBConnection();
        using (var session = _store?.OpenSession())
        {
            return session?.Load<ContentBlock>(objectID.ToString());
        }
    } 

1 个答案:

答案 0 :(得分:1)

通过反射调用泛型方法会花费一些性能,但这是可能的。

待办事项:

var result = session.Load<Entity>("abc");

反射看起来像:

MethodInfo method = typeof(Session).GetMethod("Load");
MethodInfo generic = method.MakeGenericMethod(typeof(Entity));
var result = generic.Invoke(session, new[] { "abc" });

正如你所提到的那样慢得多(在我的电脑上慢了20多倍)。

您可以通过仅执行一次艰苦工作并使用以下内容缓存反射类型的结果来提高性能:

public class SessionInvoker
{
    private Dictionary<Type, Func<Session, string, IEntity>> cache =
        new Dictionary<Type, Func<Session, string, IEntity>>();

    public IEntity Invoke(Type type, Session session, string id)
    {
        var invoker = cache.ContainsKey(type)
            ? cache[type]
            : CreateAndCache(type);

        return invoker(session, id);
    }

    private Func<Session, string, IEntity> CreateAndCache(Type type)
    {
        MethodInfo method = typeof(Session).GetMethod("Load");
        MethodInfo generic = method.MakeGenericMethod(type);
        var invoker = new Func<Session, string, IEntity>((session, id) => 
            (IEntity)generic.Invoke(session, new[] { id }));
        cache[type] = invoker;
        return invoker;
    }
}

我的电脑比直接调用方法慢4倍:

var result = invoker.Invoke(type, session, "abc");

以下是获取时间的代码:

class Program
{
    static void Main(string[] args)
    {
        var session = new Session();
        var timer = Stopwatch.StartNew();
        for (int i = 0; i < 1000000; i++)
        {
            var result = session.Load<Entity>("abc");
        }
        Console.WriteLine(timer.ElapsedMilliseconds);

        timer = Stopwatch.StartNew();
        for (int i = 0; i < 1000000; i++)
        {
            MethodInfo method = typeof(Session).GetMethod("Load");
            MethodInfo generic = method.MakeGenericMethod(typeof(Entity));
            var result = generic.Invoke(session, new[] { "abc" });
        }
        Console.WriteLine(timer.ElapsedMilliseconds);

        timer = Stopwatch.StartNew();
        MethodInfo method2 = typeof(Session).GetMethod("Load");
        MethodInfo generic2 = method2.MakeGenericMethod(typeof(Entity));
        for (int i = 0; i < 1000000; i++)
        {
            var result = generic2.Invoke(session, new[] { "abc" });
        }
        Console.WriteLine(timer.ElapsedMilliseconds);

        timer = Stopwatch.StartNew();
        var invoker = new SessionInvoker();
        var type = typeof(Entity);
        for (int i = 0; i < 1000000; i++)
        {
            var result = invoker.Invoke(type, session, "abc");
        }
        Console.WriteLine(timer.ElapsedMilliseconds);

        Console.ReadLine();
    }

    public interface IEntity { }

    public class Entity : IEntity { }

    public class Session
    {
        public IEntity Load<T>(string id) where T : IEntity, new()
        {
            return new T();
        }
    }
}