Luk*_*ina 17 c# reflection performance properties
这是使用反射更新属性的最快方法吗?假设该属性始终为int:
PropertyInfo counterPropertyInfo = GetProperty();
int value = (int)counterPropertyInfo.GetValue(this, null);
counterPropertyInfo.SetValue(this, value + 1, null);
Run Code Online (Sandbox Code Playgroud)
naw*_*fal 24
当你知道类型参数时,我在这里做了一些基准测试(非泛型方法不会非常不同).CreateDelegate如果你不能直接访问它,它将是一个属性最快的方法.随着CreateDelegate你得到一个直接句柄GetGetMethod和GetSetMethod的PropertyInfo,因此反射不是每次使用.
public static Func<S, T> BuildGetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
return propertySelector.GetPropertyInfo().GetGetMethod().CreateDelegate<Func<S, T>>();
}
public static Action<S, T> BuildSetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
return propertySelector.GetPropertyInfo().GetSetMethod().CreateDelegate<Action<S, T>>();
}
// a generic extension for CreateDelegate
public static T CreateDelegate<T>(this MethodInfo method) where T : class
{
return Delegate.CreateDelegate(typeof(T), method) as T;
}
public static PropertyInfo GetPropertyInfo<S, T>(this Expression<Func<S, T>> propertySelector)
{
var body = propertySelector.Body as MemberExpression;
if (body == null)
throw new MissingMemberException("something went wrong");
return body.Member as PropertyInfo;
}
Run Code Online (Sandbox Code Playgroud)
所以现在你打电话:
TestClass cwp = new TestClass();
var access = BuildGetAccessor((TestClass t) => t.AnyValue);
var result = access(cwp);
Run Code Online (Sandbox Code Playgroud)
或者更好的是,您可以将逻辑封装在专用类中,以便在其上设置get和set方法.
就像是:
public class Accessor<S>
{
public static Accessor<S, T> Create<T>(Expression<Func<S, T>> memberSelector)
{
return new GetterSetter<T>(memberSelector);
}
public Accessor<S, T> Get<T>(Expression<Func<S, T>> memberSelector)
{
return Create(memberSelector);
}
public Accessor()
{
}
class GetterSetter<T> : Accessor<S, T>
{
public GetterSetter(Expression<Func<S, T>> memberSelector) : base(memberSelector)
{
}
}
}
public class Accessor<S, T> : Accessor<S>
{
Func<S, T> Getter;
Action<S, T> Setter;
public bool IsReadable { get; private set; }
public bool IsWritable { get; private set; }
public T this[S instance]
{
get
{
if (!IsReadable)
throw new ArgumentException("Property get method not found.");
return Getter(instance);
}
set
{
if (!IsWritable)
throw new ArgumentException("Property set method not found.");
Setter(instance, value);
}
}
protected Accessor(Expression<Func<S, T>> memberSelector) //access not given to outside world
{
var prop = memberSelector.GetPropertyInfo();
IsReadable = prop.CanRead;
IsWritable = prop.CanWrite;
AssignDelegate(IsReadable, ref Getter, prop.GetGetMethod());
AssignDelegate(IsWritable, ref Setter, prop.GetSetMethod());
}
void AssignDelegate<K>(bool assignable, ref K assignee, MethodInfo assignor) where K : class
{
if (assignable)
assignee = assignor.CreateDelegate<K>();
}
}
Run Code Online (Sandbox Code Playgroud)
简短而简单.您可以为要获取/设置的每个"类 - 属性"对携带此类的实例.
用法:
Person p = new Person { Age = 23 };
var ageAccessor = Accessor<Person>(x => x.Age);
int age = ageAccessor[p]; //gets 23
ageAccessor[p] = 45; //sets 45
Run Code Online (Sandbox Code Playgroud)
在这里使用索引器有点不好,你可以用专用的"Get"和"Set"方法替换它,但对我来说非常直观:)
为了避免每次都要指定类型,
var ageAccessor = Accessor<Person>(x => x.Age);
var nameAccessor = Accessor<Person>(x => x.Name);
var placeAccessor = Accessor<Person>(x => x.Place);
Run Code Online (Sandbox Code Playgroud)
我使基Accessor<>类可实现,这意味着你可以做到
var personAccessor = new Accessor<Person>();
var ageAccessor = personAccessor.Get(x => x.Age);
var nameAccessor = personAccessor.Get(x => x.Name);
var placeAccessor = personAccessor.Get(x => x.Place);
Run Code Online (Sandbox Code Playgroud)
拥有基Accessor<>类意味着您可以将它们视为一种类型,例如,
var personAccessor = new Accessor<Person>();
var personAccessorArray = new Accessor<Person>[]
{
personAccessor.Get(x => x.Age),
personAccessor.Get(x => x.Name),
personAccessor.Get(x => x.Place);
};
Run Code Online (Sandbox Code Playgroud)
Bra*_*AGr 11
请确保以某种方式缓存PropertyInfo,以便您不能重复调用type.GetProperty.除此之外,如果您为执行增量的类型的方法创建委托可能会更快,或者像Teoman建议使类型实现接口并使用它.
Tho*_*mas 11
你应该看一下FastMember(nuget,源代码),它与反射相比真的很快.
我测试了这3个实现:
基准测试需要基准功能:
static long Benchmark(Action action, int iterationCount, bool print = true)
{
GC.Collect();
var sw = new Stopwatch();
action(); // Execute once before
sw.Start();
for (var i = 0; i <= iterationCount; i++)
{
action();
}
sw.Stop();
if (print) System.Console.WriteLine("Elapsed: {0}ms", sw.ElapsedMilliseconds);
return sw.ElapsedMilliseconds;
}
Run Code Online (Sandbox Code Playgroud)
假类:
public class ClassA
{
public string PropertyA { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
一些测试方法:
private static void Set(string propertyName, string value)
{
var obj = new ClassA();
obj.PropertyA = value;
}
private static void FastMember(string propertyName, string value)
{
var obj = new ClassA();
var type = obj.GetType();
var accessors = TypeAccessor.Create(type);
accessors[obj, "PropertyA"] = "PropertyValue";
}
private static void SetValue(string propertyName, string value)
{
var obj = new ClassA();
var propertyInfo = obj.GetType().GetProperty(propertyName);
propertyInfo.SetValue(obj, value);
}
private static void SetMethodInvoke(string propertyName, string value)
{
var obj = new ClassA();
var propertyInfo = obj.GetType().GetProperty(propertyName);
propertyInfo.SetMethod.Invoke(obj, new object[] { value });
}
Run Code Online (Sandbox Code Playgroud)
脚本本身:
var iterationCount = 100000;
var propertyName = "PropertyA";
var value = "PropertyValue";
Benchmark(() => Set(propertyName, value), iterationCount);
Benchmark(() => FastMember(propertyName, value), iterationCount);
Benchmark(() => SetValue(propertyName, value), iterationCount);
Benchmark(() => SetMethodInvoke(propertyName, value), iterationCount);
Run Code Online (Sandbox Code Playgroud)
10万次迭代的结果:
默认设置器:3ms
FastMember:36ms
PropertyInfo.SetValue:109ms
PropertyInfo.SetMethod:91ms
现在你可以选择你的!