リフレクションの使用でパフォーマンスの問題が発生します。
そこで、オブジェクトのプロパティのデリゲートを作成することにしました。これまでのところ、次のようになっています。
_TestClass cwp = new TestClass();
var propertyInt = typeof(TestClass).GetProperties().Single(obj => obj.Name == "AnyValue");
var access = BuildGetAccessor(propertyInt.GetGetMethod());
var result = access(cwp);
_
_static Func<object, object> BuildGetAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
Expression<Func<object, object>> expr =
Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method),
typeof(object)),
obj);
return expr.Compile();
}
_
結果は非常に満足のいくもので、従来の方法(PropertyInfo.GetValue (obj, null);
)を使用した場合よりも約30〜40倍高速でした。
問題は次のとおりです。同じように機能するプロパティのSetValue
を作成するにはどうすればよいですか?残念ながら方法がありませんでした。
アプリケーションの構造上、_<T>
_でメソッドを使用できないため、これを行っています。
これはあなたのために働くはずです:
static Action<object, object> BuildSetAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
Expression<Action<object, object>> expr =
Expression.Lambda<Action<object, object>>(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj,
value);
return expr.Compile();
}
使用法:
var accessor = BuildSetAccessor(typeof(TestClass).GetProperty("MyProperty").GetSetMethod());
var instance = new TestClass();
accessor(instance, "foo");
Console.WriteLine(instance.MyProperty);
TestClass
の場合:
public class TestClass
{
public string MyProperty { get; set; }
}
プリントアウト:
foo
パフォーマンスが重要な場合は、CreateDelegate
コンストラクトを使用したほうがよいと思います。メソッドのシグネチャ(ここではGetGetMethod
のGetSetMethod
とPropertyInfo
)を事前に知っているので、同じシグネチャでメソッドを実行するためのデリゲートを作成できます。直接。デリゲートへのロジック(メソッドハンドルがない)を構築する必要がある場合は、式の方が適しています。私はこの問題へのさまざまなルートでいくつかのベンチマークを行いました:
Func<S, T> Getter;
Action<S, T> Setter;
PropertyInfo Property;
public void Initialize(Expression<Func<S, T>> propertySelector)
{
var body = propertySelector.Body as MemberExpression;
if (body == null)
throw new MissingMemberException("something went wrong");
Property = body.Member as PropertyInfo;
//approaches:
//Getter = s => (T)Property.GetValue(s, null);
//Getter = memberSelector.Compile();
//ParameterExpression inst = Expression.Parameter(typeof(S));
//Getter = Expression.Lambda<Func<S, T>>(Expression.Property(inst, Property), inst).Compile();
//var inst = Expression.Parameter(typeof(S));
//Getter = Expression.Lambda<Func<S, T>>(Expression.Call(inst, Property.GetGetMethod()), inst).Compile();
//Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), Property.GetGetMethod());
//Setter = (s, t) => Property.SetValue(s, t, null);
//var val = Expression.Parameter(typeof(T));
//var inst = Expression.Parameter(typeof(S));
//Setter = Expression.Lambda<Action<S, T>>(Expression.Call(inst, Property.GetSetMethod(), val),
// inst, val).Compile();
//Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), Property.GetSetMethod());
}
//Actual calls (tested under loop):
public T Get(S instance)
{
//direct invocation:
//return (T)Property.GetValue(instance, null);
//calling the delegate:
//return Getter(instance);
}
public void Set(S instance, T value)
{
//direct invocation:
//Property.SetValue(instance, value, null);
//calling the delegate:
//Setter(instance, value);
}
約10000000回の呼び出しの結果 Get、Set):
GetValue-SetValue(直接):3800ミリ秒、5500ミリ秒
GetValue-SetValue(委任):3600ミリ秒、5300ミリ秒
コンパイルされた式:
Get: Expression.Property: 280 ms Expression.Call: 280 ms direct compile: 280 ms Set: 300 ms
デリゲートの作成:130ミリ秒、135ミリ秒
直接プロパティコール:70ミリ秒、70ミリ秒
あなたの場合、私は次のように書きます。
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;
}
だから今あなたは呼ぶ:
TestClass cwp = new TestClass();
var access = BuildGetAccessor((TestClass t) => t.AnyValue);
var result = access(cwp);
簡単じゃないですか?正確なことを処理するためにジェネリッククラス---(ここ を書いていました。
動的タイプを使用します。それらはボンネットの下で反射を使用しますが、はるかに高速です。
さもないと...
パーミッシブライセンスを備えた無料の高速リフレクションライブラリがたくさんあります。私はあなたをリンクしますが、多すぎて、どちらがあなたに合うかわかりません。 codeplexなどを検索するだけです。気に入ったものが見つかったら、試してみてください。
しかし、ええ、おそらくその前に、リフレクションが本当にであるかどうかを考えてください答えです。多くの場合、他の解決策があります。
編集:要求に応じて...
http://geekswithblogs.net/SunnyCoder/archive/2009/06/26/c-4.0-dynamics-vs.-reflection.aspx
http://theburningmonk.com/2010/09/performance-test-dynamic-method-invocation-in-csharp-4/
http://www.mssoftwareconsulting.com/msswc/blog/post/C-40-and-dynamic-performance.aspx
私の知る限り、それは常識です。