2010-01-30 70 views
15

有投Fun<TEntity, TId>Func<TEntity, object>更快地將Func <T, T2>投射到Func <T, object>?

public static class StaticAccessors<TEntity> 
{ 
public static Func<TEntity, TId> TypedGetPropertyFn<TId>(PropertyInfo pi) 
{ 
    var mi = pi.GetGetMethod(); 
    return (Func<TEntity, TId>)Delegate.CreateDelegate(typeof(Func<TEntity, TId>), mi); 
} 

public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi) 
{ 
    var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn"); 
    var genericMi = mi.MakeGenericMethod(pi.PropertyType); 
    var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi }); 

    //slow: lambda includes a reflection call 
    return x => typedGetPropertyFn.Method.Invoke(x, new object[] { }); //can we replace this? 
} 
} 

有沒有辦法來typedGetPropertyFnFunc<TEntity, object>轉換,而無需像上面的例子中,返回拉姆達反射代碼更快的方法?

編輯:添加修改的方案

好感謝280Z28領導我失望,我已經包括在下面的最終解決方案的正確道路。我已經爲那些不支持表達式的平臺留下了反射代碼。對於平臺,它顯示26x27x(13/.5滴答平均值)性能增加得到intstring屬性。

public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi) 
{ 
    var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn"); 
    var genericMi = mi.MakeGenericMethod(pi.PropertyType); 
    var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi }); 

    #if NO_EXPRESSIONS 
    return x => typedGetPropertyFn.Method.Invoke(x, new object[] { }); 
    #else 
    var typedMi = typedGetPropertyFn.Method; 
    var obj = Expression.Parameter(typeof(object), "oFunc"); 
    var expr = Expression.Lambda<Func<TEntity, object>> (
      Expression.Convert(
       Expression.Call(
        Expression.Convert(obj, typedMi.DeclaringType), 
        typedMi 
       ), 
       typeof(object) 
      ), 
      obj 
     ); 
    return expr.Compile(); 
    #endif 
} 

回答

6

如你所知,你可以從一個PropertyInfo.GetGetMethod()MethodInfo。從那裏,你可以使用以下來獲得Func<object, object>來檢索該屬性。通過類似的方法,你可以返回一個強類型的Func<TObject, TResult>。對於任何給定的MethodInfo,如果您不止一次需要此調用的結果,則應緩存此調用的結果,因爲此方法至少比調用所產生的委託要貴一個數量級。

private static Func<object, object> BuildAccessor(MethodInfo method) 
{ 
    ParameterExpression obj = Expression.Parameter(typeof(object), "obj"); 

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

在.NET 4.0中,您可以這樣做,因爲Func委託使用out修飾符標記TResult。 .NET 3.5不支持generic covariance/contravariance,所以你不能簡單地施放。我不確定是否有另一種比反思更快的聰明方式。

這是the .NET 4.0 doc page for Func。注意,TResult被標記爲「out」,所以它的返回值可以被轉換爲一個較不具體的類型,比如object。


對於沒有外部依賴關係的快速示例,以下代碼無法在.NET 3.5上編譯,但編譯並在.NET 4.0上正確運行。

// copy and paste into LINQpad 
void Main() 
{ 
    Func<int, string> func1 = GetString; 
    string res1 = func1(1); 
    res1.Dump(); 

    Func<int, object> func2 = func1; 
    object res2 = func2(1); 
    res2.Dump(); 
} 

public string GetString<T>(T obj) { 
    return obj.ToString(); 
} 
+0

如果我的初始函數返回像Guid這樣的值類型會怎麼樣?然後,在嘗試投射到Func 時遇到運行時錯誤。 – 2012-06-15 14:32:47

5

你有沒有考慮過做以下幾點:

Func<Foo, Bar> typed = (f) => return new Bar(); 
Func<Foo, object> untyped = (f) => typed(f); 

這樣,你只是包裝的委託。

+0

我不能這樣做,因爲我沒有編譯時類型的'Bar' – mythz 2010-01-30 19:48:00

+1

如果你在編譯時有'Bar'類型,這將是最容易和「奧林匹克」的方式來做這個XD 。 – 2013-03-25 21:24:15

相關問題