gpt4 book ai didi

.net - 如何在 Activator.CreateInstance 中传递 ctor args 或使用 IL?

转载 作者:行者123 更新时间:2023-12-04 02:03:56 25 4
gpt4 key购买 nike

我需要一个性能增强的 Activator.CreateInstance() 并且遇到了 this article Miron Abramson 使用工厂在 IL 中创建实例,然后缓存它。 (我在下面包含了来自 Miron Abramson 网站的代码,以防它以某种方式消失)。我是 IL Emit 代码的新手,除了 Activator.CreateInstance() 之外的任何用于实例化类的东西,任何帮助都会非常感激。

我的问题是我需要创建一个带有参数的 ctor 的对象实例。我看到有一种方法可以传入参数的类型,但是有没有一种方法可以传入 ctor 参数的值?

如果可能,我想使用类似于 CreateObjectFactory<T>(params object[] constructorParams) 的方法。因为我想实例化的一些对象可能有超过 1 个 ctor 参数。


// Source: http://mironabramson.com/blog/post/2008/08/Fast-version-of-the-ActivatorCreateInstance-method-using-IL.aspx
public static class FastObjectFactory
{
private static readonly Hashtable creatorCache = Hashtable.Synchronized(new Hashtable());
private readonly static Type coType = typeof(CreateObject);
public delegate object CreateObject();

///
/// Create an object that will used as a 'factory' to the specified type T
///
public static CreateObject CreateObjectFactory() where T : class
{
Type t = typeof(T);
FastObjectFactory.CreateObject c = creatorCache[t] as FastObjectFactory.CreateObject;
if (c == null)
{
lock (creatorCache.SyncRoot)
{
c = creatorCache[t] as FastObjectFactory.CreateObject;
if (c != null)
{
return c;
}
DynamicMethod dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + t.Name, typeof(object), null, t);
ILGenerator ilGen = dynMethod.GetILGenerator();

ilGen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
ilGen.Emit(OpCodes.Ret);
c = (CreateObject)dynMethod.CreateDelegate(coType);
creatorCache.Add(t, c);
}
}
return c;
}
}


Update to Miron's code from commentor on his post 2010-01-11
public static class FastObjectFactory2<T> where T : class, new()
{
public static Func<T> CreateObject { get; private set; }

static FastObjectFactory2()
{
Type objType = typeof(T);
var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
ILGenerator ilGen = dynMethod.GetILGenerator();
ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
ilGen.Emit(OpCodes.Ret);
CreateObject = (Func<T>)
dynMethod.CreateDelegate(typeof(Func<T>));
}
}

最佳答案

我一直在对此进行一些测试,作为 Miron 原始文章 (here) 的后续内容,我发现 .NET 4.0 Activator 比以前快得多。他的应用程序版本的一些结果经过调整以显示以毫秒为单位的时间:

.NET 3.5 build

Number of iterates: 1000000
Activator.CreateInstance(Type): 4150
Activator.CreateInstance<T>(): 1288
FastObjectFactory.CreateObjec (empty cache): 33
FastObjectFactory.CreateObjec (cache full): 28
ItemFactory.GetNewItem: 1283


.NET 4.0 build

Number of iterates: 1000000
Activator.CreateInstance(Type): 138
Activator.CreateInstance<T>(): 151
FastObjectFactory.CreateObjec (empty cache): 28
FastObjectFactory.CreateObjec (cache full): 22
ItemFactory.GetNewItem: 156

然而,这是针对无参数构造函数的,我还注意到,当使用带有参数的构造函数时,激活器仍然有点慢,如下所示。

我在这里发布的原始解决方案遇到的一个问题是,我不一定知道在编译时我想要的对象的类型 - 我只有一个类型引用。现在(除非我是个笨蛋)这意味着我不能在这里使用通用解决方案或它的简单变体。

所以这是我拼凑的一个版本,它解决了这个问题。当使用构造函数参数时,它也显示了 .NET 4.0 Activator 中的轻微缓慢:
// For use with no-parameter constructors. Also contains constants and utility methods
public static class FastActivator
{
// THIS VERSION NOT THREAD SAFE YET
static Dictionary<Type, Func<object>> constructorCache = new Dictionary<Type, Func<object>>();

private const string DynamicMethodPrefix = "DM$_FastActivator_";

public static object CreateInstance(Type objType)
{
return GetConstructor(objType)();
}

public static Func<object> GetConstructor(Type objType)
{
Func<object> constructor;
if (!constructorCache.TryGetValue(objType, out constructor))
{
constructor = (Func<object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<object>), new Type[] { });
constructorCache.Add(objType, constructor);
}
return constructor;
}

public static object BuildConstructorDelegate(Type objType, Type delegateType, Type[] argTypes)
{
var dynMethod = new DynamicMethod(DynamicMethodPrefix + objType.Name + "$" + argTypes.Length.ToString(), objType, argTypes, objType);
ILGenerator ilGen = dynMethod.GetILGenerator();
for (int argIdx = 0; argIdx < argTypes.Length; argIdx++)
{
ilGen.Emit(OpCodes.Ldarg, argIdx);
}
ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(argTypes));
ilGen.Emit(OpCodes.Ret);
return dynMethod.CreateDelegate(delegateType);
}
}

// For use with one-parameter constructors, argument type = T1
public static class FastActivator<T1>
{
// THIS VERSION NOT THREAD SAFE YET
static Dictionary<Type, Func<T1, object>> constructorCache = new Dictionary<Type, Func<T1, object>>();
public static object CreateInstance(Type objType, T1 arg1)
{
return GetConstructor(objType, new Type[] { typeof(T1) })(arg1);
}
public static Func<T1, object> GetConstructor(Type objType, Type[] argTypes)
{
Func<T1, object> constructor;
if (!constructorCache.TryGetValue(objType, out constructor))
{
constructor = (Func<T1, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, object>), argTypes);
constructorCache.Add(objType, constructor);
}
return constructor;
}
}

// For use with two-parameter constructors, argument types = T1, T2
public static class FastActivator<T1, T2>
{
// THIS VERSION NOT THREAD SAFE YET
static Dictionary<Type, Func<T1, T2, object>> constructorCache = new Dictionary<Type, Func<T1, T2, object>>();
public static object CreateInstance(Type objType, T1 arg1, T2 arg2)
{
return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2) })(arg1, arg2);
}

public static Func<T1, T2, object> GetConstructor(Type objType, Type[] argTypes)
{
Func<T1, T2, object> constructor;
if (!constructorCache.TryGetValue(objType, out constructor))
{
constructor = (Func<T1, T2, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, object>), argTypes);
constructorCache.Add(objType, constructor);
}
return constructor;
}
}

// For use with three-parameter constructors, argument types = T1, T2, T3
// NB: could possibly merge these FastActivator<T1,...> classes and avoid generic type parameters
// but would need to take care that cache entries were keyed to distinguish constructors having
// the same number of parameters but of different types. Keep separate for now.
public static class FastActivator<T1, T2, T3>
{
// THIS VERSION NOT THREAD SAFE YET
static Dictionary<Type, Func<T1, T2, T3, object>> constructorCache = new Dictionary<Type, Func<T1, T2, T3, object>>();
public static object CreateInstance(Type objType, T1 arg1, T2 arg2, T3 arg3)
{
return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2), typeof(T3) })(arg1, arg2, arg3);
}

public static Func<T1, T2, T3, object> GetConstructor(Type objType, Type[] argTypes)
{
Func<T1, T2, T3, object> constructor;
if (!constructorCache.TryGetValue(objType, out constructor))
{
constructor = (Func<T1, T2, T3, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, T3, object>), argTypes);
constructorCache.Add(objType, constructor);
}
return constructor;
}
}

下面的一些性能结果。请注意,这是为了再次以毫秒为单位创建 100 万个对象和计时:
Activator.CreateInstance(objType) - parameterless constructor: 153
FastActivator.CreateInstance(objType) - parameterless constructor: 86
Using FastActivator.GetConstructor and calling it repeatedly - parameterless constructor: 34
Activator.CreateInstance(objType) with 1 constructor arg: 3183
FastActivator.CreateInstance(objType) with 1 constructor arg: 257
FastActivator.GetConstructor and calling it repeatedly with 1 constructor arg: 126
Activator.CreateInstance(objType) with 3 constructor args: 4403
FastActivator.CreateInstance(objType) with 3 constructor args: 640
FastActivator.GetConstructor and calling it repeatedly with 3 constructor args : 405
FastActivator.GetConstructor and calling it repeatedly with 3 constructor args; args created only once : 19

关于.net - 如何在 Activator.CreateInstance 中传递 ctor args 或使用 IL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2024435/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com