2011-05-31 37 views
5

我遇到了一個場景,我需要根據輸入對不同屬性上的自定義類型列表進行排序。在幾篇文章的幫助下,我能夠使用LINQ提出泛型實現。在單元測試中,其中一個測試失敗,因爲使用表達式樹創建lamda表達式時發生隱式轉換。表達式樹中沒有發生隱式轉換

下面我把示例代碼來了解這個問題(不知道爲什麼格式沒有得到正確的,遺憾的是)

static class ExtensionMethods 
{ 
public static IEnumerable<TSource> Sort<TSource>(this IEnumerable<TSource> unSortedList, Func<TSource, object> selector, bool isAscending) 
    { 
     return isAscending ? unSortedList.OrderBy(selector) :     unSortedList.OrderByDescending(selector); 
} 
} 

class Program 
{ 

    class Student 
    { 
     public string Name { get; set; } 
     public int Age { get; set; } 
    } 

    static void Main(string[] args) 
    { 
     var unOrderedStudents = new List<Student> 
          { 
           new Student{ Name="A", Age=20}, 
           new Student{Name = "B", Age=19} 
          }; 


     //This Works 
     var sortUsingLamda = unOrderedStudents.Sort<Student>(stud => stud.Age, true); 


     //Exception - Expression of type 'System.Int32' cannot be used for return type 'System.Object' 
     var sortUsingExpressionTree = unOrderedStudents.Sort<Student>(GetSortFunc<Student>("Age"), true); 

     Console.WriteLine("Press any key to continue"); 
     Console.ReadLine(); 
    } 



    private static Func<T, object> GetSortFunc<T>(string sortColumn) 
    { 
     var param = Expression.Parameter(typeof(T), "entity"); 

     var propertyExpression = Expression.Property(param, sortColumn); 

     var boxingExpression = Expression.Convert(propertyExpression, typeof(object)); 

     return Expression.Lambda<Func<T, object>>(propertyExpression, param).Compile(); 

     //after adding Convert expression issue got fixed 
     //return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile(); 

    } 
} 

在Main方法,當我嘗試直接傳遞函數求代表排序擴展方法它的工作原理,但它與表達式樹失敗。

我發現了一個similar問題,但討論了約束類型參數。這兩個問題是否一樣?有人能幫我理解這個問題嗎?

回答

5

您需要使用盒裝版(您目前創建boxingExpression,但基地最終查詢,而不是在propertyExpression):

return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile(); 

爲什麼這不是隱含的 - 只有這裏沒有隱式轉換; Expression!= C#。拳擊是一個非平凡的操作,並且Expression API需要樹中的特定節點。

+0

這很有道理。如果我沒有錯,隱式轉換是在編譯時完成的,運行時完全沒有意識到這一點。由於表達式樹在運行時執行,因此需要有一個轉換節點來轉換它。如果我沒有正確理解,請糾正我。 – Moorthy 2011-05-31 16:12:25

+0

@Moorthy - 是的,拳擊是編譯器中的explit(有一個操作碼)。然而,保持* reference-preserving *強制轉換爲基本類型是合法的,並且是無操作的(完全沒有任何操作) - 您可能會發現它的工作正常。 – 2011-05-31 16:42:26

1

您有Func<TSource, object> selector參數。 這意味着你有功能接收TSource對象並返回object。 所以,你需要編譯boxingExpression並返回結果:

 return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile(); 
+0

那是對的。但是,當我直接將Func 傳遞給Sort方法時,它可以工作。我不必做明確的演員。 – Moorthy 2011-05-31 12:49:01

+0

已更新答案 – VMAtm 2011-05-31 12:55:26

2

你原型GetSortFunc爲返回Func鍵<>實例,它返回一個對象。因此,確保您生成的表達式樹生成對象是您的工作。

雖然INT在隱式轉換成對象在C#引擎蓋下它被裝箱。這就是爲什麼你需要在你的代碼中使用裝箱表達式,以及爲什麼你需要使用從Expression.Convert得到的表達式來生成lambda表達式。思考這個問題的最佳方式是,在使用表達式樹時,必須明確所有轉換,而不是從編寫C#代碼的方式來考慮它。