gpt4 book ai didi

c# - 使用 lambda 表达式创建带反射的对象

转载 作者:行者123 更新时间:2023-11-30 12:20:50 28 4
gpt4 key购买 nike

我们通过调用 Activator.CreateInstance 在我们的代码中创建新对象来使用大量反射,但是基于 this article 最好使用已编译的 lambda 表达式来提高性能。所以我创建了一个静态函数,它使用 lambda 表达式创建一个类的实例:

    public static class ClassBuilder
{

private delegate T ObjectActivator<T>(params object[] args);

/// <summary>
/// This function will create a concrete object of type T
/// </summary>
/// <typeparam name="T">Base or concrete of object to return</typeparam>
/// <param name="type">Concrete type of the object to create</param>
/// <param name="parameters">paramters to give to the constructor</param>
/// <returns>Instance of the concrete object</returns>
public static T CreateInstance<T>(Type type, params object[] parameters)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}

// get the concrete types of given params
Type[] typedArgs = new Type[parameters.Length];

for (int i = 0; i < parameters.Length; i++)
{
typedArgs[i] = parameters[i].GetType();
}

// get the right constructor depending the arguments
ConstructorInfo ctor = type.GetConstructor(typedArgs);

if (ctor != null)
{
// create the activator
ObjectActivator<T> createdActivator = GetActivator<T>(ctor);

// return the concrete object
return createdActivator(parameters);
}
else
{
throw new ArgumentException("Unable to find constructor with specified parameters.");
}
}


private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
{

Type type = ctor.DeclaringType;
ParameterInfo[] paramsInfo = ctor.GetParameters();

// create parameter of name args as expression (type of args : object[])
ParameterExpression param = Expression.Parameter(typeof(object[]), "args");

// create expressions for all the parameters of the constructor with the right type
Expression[] argsExp = new Expression[paramsInfo.Length];
for (int i = 0; i < paramsInfo.Length; i++)
{
// get the type of the current parameter (parameter a position i)
Expression idx = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;

Expression paramAccessorExp = Expression.ArrayIndex(param, idx);

// Creates a UnaryExpression that represents a type conversion operation.
argsExp[i] = Expression.Convert(paramAccessorExp, paramType);
}

// Creates a NewExpression that represents calling the specified constructor with the specified arguments.
NewExpression newExp = Expression.New(ctor, argsExp);

// Creates a LambdaExpression by first constructing a delegate type.
LambdaExpression lambdaExpression = Expression.Lambda(typeof(ObjectActivator<T>), newExp, param);

// Compile function will create a delegate function that represents the lamba expression
ObjectActivator<T> compiledExpression = (ObjectActivator<T>)lambdaExpression.Compile();

return compiledExpression;
}
}

但在此实现之后,我尝试通过创建对象的 1000 个实例来分析 3 种方法(Activator.CreateInstance、Inovke 和 Lambda Expression)。我真的对 lambda 表达式的结果感到失望。

然后我在that blog中看到“......重要的是要记住编译应该只执行一次,因此应该仔细检查代码以避免偶尔重新编译 lambda”

所以我添加了一个缓存,将构造函数信息作为字典的键,如下所示:

    // declaration
private static ConcurrentDictionary<object, object> _activatorCache = new ConcurrentDictionary<object, object>();


private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
{
// check if the object is in the cache before creating it
if (_activatorCache.ContainsKey(ctor))
{
return _activatorCache[ctor] as ObjectActivator<T>;
}


Type type = ctor.DeclaringType;
ParameterInfo[] paramsInfo = ctor.GetParameters();

...

// Compile function will create a delegate function that represents the lamba expression
ObjectActivator<T> compiledExpression = (ObjectActivator<T>)lambdaExpression.Compile();

// add the compiled expression to the cache
_activatorCache[ctor] = compiledExpression;
}

它提高了很多性能,但仍然不一致。

我做错了什么吗?

这是一种以构造函数信息为键来缓存已编译表达式的好方法吗?

最佳答案

您的代码在检查缓存之前仍然进行了相当多的反射 - GetConstructor 之类的东西不是免费的。相反,你也许可以寻找类似的东西:

public static T CreateInstance<T>(...) // ...=whatever args
{
return CtorCache<T>.Ctor(...); // ...=whatever args
}
private static class CtorCache<T> {
public static Func<..., T> Ctor; // ...=whatever args
static CtorCache() {
// TODO: assign CTOR - either through reflection code,
// or as a fallback something like:
// Ctor = args => Activator.CreateInstance<T>();
}
}

这在一般情况下没有起作用——如果静态构造函数已经完成,它只是访问字段并调用委托(delegate)。它甚至避免了字典查找(或者更确切地说:它将其推送到由 CLR 本身实现的类似内容)。

关于c# - 使用 lambda 表达式创建带反射的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49452551/

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