我正在努力提高下面代码的性能,有点知道如何,但不确定哪是最好的方法。第一次命中会花更长的时间,但随后的命中会更快。现在,我可以缓存T(这里T是一个类),然后检查缓存,看看"T"是否存在,如果存在,继续获取它的相关信息(NamedArguments),并遍历每个NamedArguments,最后如果条件匹配,继续设置当前属性的值。
我只是想让它更有效率和性能。什么好主意吗?
var myProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(prop => Attribute.IsDefined(prop, typeof(MyCustomAttribute)) && prop.CanWrite && prop.GetSetMethod() != null);
foreach (var currentProperty in myProps)
{
foreach (var currentAttributeForProperty in currentProperty.GetCustomAttributesData())
{
foreach (var currentNamedArgument in currentAttributeForProperty.NamedArguments)
{
if (string.Equals(currentNamedArgument.MemberInfo.Name, "PropName", StringComparison.OrdinalIgnoreCase))
{
currentAttribParamValue = currentNamedArgument.TypedValue.Value == null ? null : currentNamedArgument.TypedValue.Value.ToString();
// read the reader for the currentAttribute value
if (reader.DoesFieldExist(currentAttribParamValue))
{
var dbRecordValue = reader[currentAttribParamValue] == DBNull.Value ? null : reader[currentAttribParamValue];
// set it in the property
currentProperty.SetValue(val, dbRecordValue, null);
}
break;
}
}
}
}
DynamicMethods或ExpressionTrees将比反射快得多。您可以为一个类型构建一个所有属性getter/setter的缓存,然后将该信息缓存在Dictionary
(或ConcurrentDictionary
)中,并将type作为键。
- Expression Tree Basics 动态方法
- 发现类型信息(例如在应用程序启动时)
- 为每个属性编译动态方法(一次执行所有属性)
- 将这些方法存储在元数据类中(示例如下)。
- 将元数据缓存到某个地方(即使是静态字段也可以,只要访问是同步的)。使用类型作为键。
- 在需要时获取类型的元数据
- 查找合适的getter/setter。
- 调用,传递您希望操作的实例。
// Metadata for a type
public sealed class TypeMetadata<T> {
// The compiled getters for the type; the property name is the key
public Dictionary<string, Func<T, object>> Getters {
get;
set;
}
// The compiled setters for the type; the property name is the key
public Dictionary<string, Action<T, object>> Setters {
get;
set;
}
}
// rough invocation flow
var type = typeof( T);
var metadata = _cache[type];
var propertyName = "MyProperty";
var setter = metadata[propertyName];
var instance = new T();
var value = 12345;
setter( instance, value );
例子Setter
摘自动态方法实现(关于这个主题的好文章)。
我不能保证这段完全正确的代码有效,但是我自己写过非常类似的代码。如果你不习惯使用IL,一定要考虑使用表达式树。
public static LateBoundPropertySet CreateSet(PropertyInfo property)
{
var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(object), typeof(object) }, true);
var gen = method.GetILGenerator();
var sourceType = property.DeclaringType;
var setter = property.GetSetMethod(true);
gen.Emit(OpCodes.Ldarg_0); // Load input to stack
gen.Emit(OpCodes.Castclass, sourceType); // Cast to source type
gen.Emit(OpCodes.Ldarg_1); // Load value to stack
gen.Emit(OpCodes.Unbox_Any, property.PropertyType); // Unbox the value to its proper value type
gen.Emit(OpCodes.Callvirt, setter); // Call the setter method
gen.Emit(OpCodes.Ret);
var result = (LateBoundPropertySet)method.CreateDelegate(typeof(LateBoundPropertySet));
return result;
}
*我的经验是快25-100倍
反射在循环中是出了名的慢,所以某种缓存可能会有所帮助。但是要决定缓存什么,应该度量。俗话说:"过早优化是万恶之源";你应该确定你真的需要优化,以及优化的具体内容。
对于更具体的建议,属性是在编译时附加的,因此您可以缓存类型和它的propertyinfo列表。
我也有过一次类似的问题-反射非常慢。我使用缓存,就像你计划的那样,性能增长超过10倍。这再也不是性能的瓶颈
我之前创建了类似的逻辑,在那里我为遇到的每种类型缓存了一个"执行计划"。对于随后的运行来说,这无疑是更快的,但您必须对您的场景进行分析,以确定是否值得额外的代码复杂性和内存使用。