我们通过调用Activator.CreateInstance在我们的代码中创建新对象来进行大量反射,但基于this文章,最好使用已编译的lambda表达式来提高性能。 所以我创建了一个静态函数,它创建了一个带有lambda表达式的类的intance:
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中看到“......重要的是要记住编译只应执行一次,因此应仔细检查代码以避免偶尔重新编译lambdas”。
所以我添加了一个缓存,它将ctor信息作为字典的键,如下所示:
// 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;
}
它提高了很多性能,但仍然没有结论。
我有什么问题吗?
这是使用ctor信息作为密钥缓存已编译表达式的好方法吗?
答案 0 :(得分:2)
你的代码仍然会在之前进行相当多的反思它会检查缓存 - 像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>();
}
}
这在一般情况下 no 工作 - 如果静态构造函数已经竞争,它只是访问该字段并调用该委托。它甚至避免了字典查找(或者更确切地说:它将它推送到CLR本身实现的类似的东西)。