使用反射优化对象创建

本文关键字:对象 创建 优化 反射 | 更新日期: 2023-09-27 17:56:17

我正在尝试优化我们遗留代码中一个类的性能,该代码使用反射来创建各种视图。我宁愿我们根本不使用反射,但在短期内删除它不是一种选择。代码来自 MVC# 框架。在这里:

public class CreateHelper
{
    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    #region Documentation
    /// <summary>
    /// Creates an object of specified type with parameters passed to the constructor.
    /// </summary>
    #endregion
    public static object Create(Type t, params object[] parameters)
    {
        Type[] paramTypes = new Type[parameters.Length];
        for (int i = 0; i < parameters.Length; i++)
            paramTypes[i] = parameters[i].GetType();
        return t.GetConstructor(paramTypes).Invoke(parameters);
    }
}

我希望尽快实现这两种方法。我阅读了 Ayende 关于对象创建优化的这篇很棒的文章,并试图根据我的目的修改他的示例,但我对 IL 的了解不存在。

我得到一个VerificationException 操作可能会破坏运行时的稳定性。Create方法中。有谁知道问题出在哪里?我可以使用此方法是否有更快的实现?这是我的尝试:

public class Created
{
    public int Num;
    public string Name;
    public Created()
    {
    }
    public Created(int num, string name)
    {
        this.Num = num;
        this.Name = name;
    }
}
public class CreateHelper
{
    private delegate object CreateCtor();
    private static CreateCtor createdCtorDelegate;
    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        var ctor = t.GetConstructor(new Type[] { });
        var method = new DynamicMethod("CreateIntance", t, new Type[] { typeof(object[]) });
        var gen = method.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_0);//arr
        gen.Emit(OpCodes.Call, ctor);// new Created
        gen.Emit(OpCodes.Ret);
        createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));
        return createdCtorDelegate(); // <=== VerificationException Operation could destabilize the runtime.
    }
    #region Documentation
    /// <summary>
    /// Creates an object of specified type with parameters passed to the constructor.
    /// </summary>
    #endregion
    public static object Create(Type t, params object[] parameters)
    {
        Type[] paramTypes = new Type[parameters.Length];
        for (int i = 0; i < parameters.Length; i++)
            paramTypes[i] = parameters[i].GetType();
        return t.GetConstructor(paramTypes).Invoke(parameters);
    }
}

然后我像这样使用类:

class Program
{
    private static Created CreateInstance()
    {
        return (Created)CreateHelper.Create(typeof(Created));
        //return new Created();
    }
    static void Main(string[] args)
    {
        int iterations = 1000000;
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            CreateInstance();
        }
        Console.WriteLine(watch.Elapsed);
        Console.ReadLine();
    }
}

更新 1

我做了一些时间:

    new Created() : 00:00
  • :00.0225015
  • Activator.CreateInstance<Created>() : 00:00
  • :00.1232143
  • (Created)CreateHelper.Create(typeof(Created)) : 00:00
  • :00.3946555

  • new Created(i, i.ToString()) : 00:00
  • :00.1476882

  • (Created)Activator.CreateInstance(typeof(Created), new object[]{ i, i.ToString() }) : 00:
  • 00:01.6342624
  • (Created)CreateHelper.Create(typeof(Created), new object[] {i, i.ToString()}) : 00:
  • 00:01.1591511

更新 2

对于默认构造函数情况,@Brannon建议的解决方案有效,但是获得的时间是 00:00:00.1165000,这不是一个巨大的改进。在这里:

public class CreateHelper
{
    private delegate object DefaultConstructor();
    private static readonly ConcurrentDictionary<Type, DefaultConstructor> DefaultConstructors = new ConcurrentDictionary<Type, DefaultConstructor>();
    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        DefaultConstructor defaultConstructorDelegate;
        if (!DefaultConstructors.TryGetValue(t, out defaultConstructorDelegate))
        {
            var ctor = t.GetConstructor(Type.EmptyTypes);
            var method = new DynamicMethod("CreateIntance", t, Type.EmptyTypes);
            var gen = method.GetILGenerator();
            gen.Emit(OpCodes.Nop);
            gen.Emit(OpCodes.Newobj, ctor);
            gen.Emit(OpCodes.Ret);
            defaultConstructorDelegate = (DefaultConstructor)method.CreateDelegate(typeof(DefaultConstructor));
            DefaultConstructors[t] = defaultConstructorDelegate;
        }
        return defaultConstructorDelegate.Invoke();
    }
}

更新 3

使用编译的表达式 Expression.New 也产生了非常好的结果 (00:00:00.1166022)。这是代码:

public class CreateHelper
{        
    private static readonly ConcurrentDictionary<Type, Func<object>> DefaultConstructors = new ConcurrentDictionary<Type, Func<object>>();
    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        Func<object> defaultConstructor;
        if (!DefaultConstructors.TryGetValue(t, out defaultConstructor))
        {
            var ctor = t.GetConstructor(Type.EmptyTypes);
            if (ctor == null)
            {
                throw new ArgumentException("Unsupported constructor for type " + t);
            }
            var constructorExpression = Expression.New(ctor);
            var lambda = Expression.Lambda<Func<Created>>(constructorExpression);
            defaultConstructor = lambda.Compile();
            DefaultConstructors[t] = defaultConstructor;
        }
        return defaultConstructor.Invoke();
    }
    #region Documentation
    /// <summary>
    /// Creates an object of specified type with parameters passed to the constructor.
    /// </summary>
    #endregion
    public static object Create(Type t, params object[] parameters)
    {
        return null;
    }
}

总结

对于默认构造函数情况,以下是摘要:

    (Created)CreateHelper.Create(typeof(Created)) : 00:00
  • :00.3946555
  • new Created() : 00:00
  • :00.0225015
  • Activator.CreateInstance<Created>() : 00:00
  • :00.1232143
  • DynamicMethod : 00:00
  • :00.1165000
  • Expression.New : 00:00
  • :00.1131143

使用反射优化对象创建

首先,您可能应该禁用该特定测试的垃圾回收器。它可能会妨碍您的结果。或者,您可以将所有创建的实例放入一个数组中。我不确定将ToString()称为其中的一部分是否有帮助。

你对花哨的构造函数代码的计划不一定是正确的计划。构造函数查找本身非常慢。应使用 Type 键将委托缓存在字典中。大多数 IoC 容器会自动执行此操作(缓存和构造)。我认为使用其中之一在您的情况下会证明是有价值的。事实上,较新的 JSON 框架还缓存构造函数信息,以便快速创建对象。也许像 Json.Net 或ServiceStack.Text这样的东西会有所帮助。

您正在构建多少种不同的类型?(我知道你的例子只显示了一个。

我不确定您在动态方法上的参数是否正确。这段代码(如下)对我来说是稳定的。不要在值类型或数组上调用它。

DynamicMethod dm = new DynamicMethod("MyCtor", type, Type.EmptyTypes, typeof(ClassFactory).Module, true);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Nop);
ilgen.Emit(OpCodes.Newobj, ci);
ilgen.Emit(OpCodes.Ret);
CtorDelegate del = ((CtorDelegate) dm.CreateDelegate(typeof(CtorDelegate)));
return del.Invoke(); // could cache del in a dictionary