2012-05-02 21 views
3

如何爲可以處理未知類型的函數創建lambda表達式?對不起,我知道這個問題很模糊,我很難形成它。我只希望你有一分鐘​​的時間,並閱讀我的故事,這應該會讓事情變得更加清晰。如何創建可以處理或投射未知類型的lambda表達式?

我的目標是使用預定義的數據合約將字符串值數組反序列化爲對象。數據合同的成員有一個職位編號。反序列化器的簡單工作是將值映射到數據成員(在進行適當的類型轉換之後)並構建對象。

問題是,反序列化性能很糟糕!運行VS Profiler後,我發現用於填充對象成員的PropertyInfo.SetValue()花費了大量時間。我的程序必須在任何給定時間反序列化數千個對象。數據合同通常有100個成員。所以我們正在對每個1000個對象說100,000次SetValue()調用,並且正在拖動。下面是調用的SetValue的樣本:

// for each data contract type 
// go through each property and set the value 
foreach(PropertyInfo pi in pis) 
{ 
    object data = convertStringToMemberType(pi, attributeArray, valueStringArray); 
    pi.SetValue(objectToBuild, data, null); 
} 

後來我發現this page from Unknown Recipes,其中有一個希望解決這個性能問題。 它看起來像我需要使用編譯的lambda表達式來替換SetValue,但我遇到了鑄造問題。以上面的鏈接爲例,我現在已經替換了SetValue()。替換項是Action委託,它們是編譯的lambda表達式。

首先,我擴展了PropertyInfo類。

public static class PropertyInfoExtensions 
{ 

    public static Action<object, object> GetValueSetter(this PropertyInfo propertyInfo) 
    { 
     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var argument = Expression.Parameter(typeof(object), "a"); 
     var setterCall = Expression.Call(
      instance, 
      propertyInfo.GetSetMethod(), 
      Expression.Convert(argument, propertyInfo.PropertyType)); 
     return (Action<object, object>)Expression.Lambda(setterCall, instance, argument).Compile(); 
    } 
} 

然後我建了一個Dictionary<PropertyInfo, Action<object, object>對象,它關係到每個PropertyInfo對象到其相應的Action委託。這樣我就可以「緩存」已編譯的lambda表達式並在一批反序列化中重用它。這是我現在該怎麼稱呼它:

foreach(PropertyInfo pi in pis) 
{ 
    object data = convertStringToMemberType(pi, attributeArray, valueStringArray); 
    var setValueDelegate = _actionDelegateDict[pi]; 
    setValueDelegate(objectToBuild, data); 
} 

不過,我收到以下異常:

Unable to cast object of type 'System.Action`2[Test.DataContract1,System.Object]' to type 'System.Action`2[System.Object,System.Object]'. 

這裏DataContract1是我試圖建立對象的類型。它只在運行時才知道,這與Unknown Recipes的例子中的情況不同,在這種情況下,類型在編譯時已知。你將如何去做這個lambda表達式的工作?

非常感謝你的時間!

+1

這不是Java。 – Jeffrey

+0

有趣的是,lamba表達式將成爲Java 8的一個特性。 – arshajii

+0

對不起,我沒有意識到我需要標記「c#」。感謝您的關注! – Zoomzoom

回答

3

聽起來很像我用我的FastReflection庫做的。你幾乎在那裏,你只需要將實例參數改爲對象類型,然後對該表達式進行強制類型轉換即可。

我認爲你現在的代碼將工作,如果它改變了這一點。

public static class PropertyInfoExtensions 
{ 
    public static Action<object, object> GetValueSetter(this PropertyInfo propertyInfo) 
    { 
     var instance = Expression.Parameter(typeof(object), "i"); 
     var argument = Expression.Parameter(typeof(object), "a"); 
     var setterCall = Expression.Call(
      Expression.Convert(instance, propertyInfo.DeclaringType), 
      propertyInfo.GetSetMethod(), 
      Expression.Convert(argument, propertyInfo.PropertyType)); 
     return Expression.Lambda<Action<object,object>>(setterCall, instance, argument).Compile(); 
    } 
} 
+0

謝謝,我會檢查你的API。至於你的建議,不幸的是我不能遵循這個指令,因爲我不知道編譯時的目標類型。當然,下一個問題是:「是否有方法將類型轉換爲Type對象?」我在這裏看到了這個問題。該解決方案似乎只是反射的用法,我不確定可以在這種情況下應用......您怎麼看? – Zoomzoom

+0

給你的代碼增加了修正,但它是免費的C#代碼,所以可能無法編譯。 –

+0

Expression.Convert做到了!謝謝Darren! – Zoomzoom

4

想象一下,你直接創建這個拉姆達,不使用Expression秒。那看起來怎麼樣?你想創建Action<object, object>

Action<object, object> action = (object i, object a) => i.Property = a; 

但是,這是行不通的,你需要投兩ia。所以:

Action<object, object> action = 
    (object i, object a) => ((DataContract)i).Property = (PropertyType)a; 

在代碼中,你鑄造a,但你需要投i太:

public static Action<object, object> GetValueSetter(this PropertyInfo propertyInfo) 
{ 
    var instance = Expression.Parameter(typeof(object), "i"); 
    var argument = Expression.Parameter(typeof(object), "a"); 
    var setterCall = Expression.Call(
     Expression.Convert(instance, propertyInfo.DeclaringType), 
     propertyInfo.GetSetMethod(), 
     Expression.Convert(argument, propertyInfo.PropertyType)); 
    return (Action<object, object>)Expression.Lambda(setterCall, instance, argument).Compile(); 
} 
+0

謝謝svick。我其實已經嘗試過。替換 var instance = Expression.Parameter(propertyInfo.DeclaringType,「i」); 與 var instance = Expression.Parameter(typeof(object),「i」); 問題是,propertyInfo.DeclaringType在這種情況下是必需的。它是包含我試圖填充的屬性的類型。如果沒有指定,那麼lambda不會編譯。 ArgumentException將是「類型'Test.DataContract1'上聲明的方法'無效set_Success(布爾)'不能用'System.Object'類型的實例調用」 – Zoomzoom

+0

問題的根源是我無法定義對象的類型我在編譯時填充它的屬性。如果它是一個已知類型,那麼我可以從未知食譜中插入n-chug示例,其中lambda的返回類型爲Action ,其中T是已知的。 :( – Zoomzoom

+0

就像我說過的,你也需要將它施放,在我的代碼中看到'Expression.Convert(instance,...)' – svick