2012-05-30 38 views
21

我遇到使用反射的性能問題。
所以我決定創建我的對象的屬性,代表和這麼遠的:反射性能 - 創建委託(屬性C#)

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(); 
} 

的結果是非常令人滿意的,比使用傳統方法快了約30-40倍( PropertyInfo.GetValue (obj, null);

問題是:我該如何製作一個屬性的SetValue,它的工作方式相同?不幸的是沒有辦法。

我這樣做是因爲我的應用程序的結構不能使用<T>的方法。

+0

「我這樣做是因爲我的應用程序的結構不能使用」< T >「的方法」 - 這是否意味着您的NETFX版本<2.0?爲什麼你不能在你的應用程序中使用泛型? –

+0

另外,爲您的屬性創建委託的過程與反射有關,您嘗試使用反射解決什麼問題? –

+0

代表具有更好的性能,可以動態使用。當您需要使用動態調用時,它們是首選選項。 – GregRos

回答

15

這應該爲你工作:

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

2

使用動態類型。他們在引擎蓋下使用反射,但他們很快

否則......

有噸的免費更快反射庫在那裏與寬容的許可。我會聯繫你,但是太多了,我不確定哪個適合你。只要搜索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

這是常識,據我可以告訴。

+0

對不起?爲什麼是負面投票? – GregRos

+1

動力學不應該用於反射,國際海事組織,除非**沒有其他方式**。你能發佈一些支持*的數據嗎?他們快很多*? ...以及OP請求中使用動態的一個示例?這些可能是負面投票的一些原因... – IAbstract

+0

他正在使用動態調用。例如。設置方法,屬性等等。這完全是**你應該對動態做什麼。 – GregRos

10

我認爲如果性能是關鍵,你會更好用CreateDelegate構造。由於您事先知道該方法的簽名,因此您可以創建一個代理以直接執行具有相同簽名的方法,這裏只是GetGetMethodGetSetMethodPropertyInfo。如果需要爲委託構建一些邏輯(您沒有方法句柄),表達式會更適合。我做了不同的路線一些基準測試,這一問題:

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); 
} 

結果1000多電話 - (獲取,設置):

的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); 

難道不是那麼簡單呃?已經寫了一個通用類here來處理確切的事情。