2016-07-01 86 views
0

我有一點挑戰。我需要對對象列表進行排序,並且我需要從代表任何子類中屬性路徑的字符串對它進行排序。 我需要使用List.Sort()而不是OrderBy()。List.Sort屬性名稱 - 包括子類

不要舉個簡單的例子。我有一個由兩個子類別代表的名稱和名稱

public class NameParts 
{ 
    public String FirstName { get; set; } 
    public String LastName { get; set; } 
} 

public class Identification 
{ 
    public String NiNumber { get; set; } 
    public NameParts Name { get; set; } 
} 

public class Person 
{ 
    public String Email { get; set; } 
    public String Phone { get; set; } 
    public Int16 Age { get; set; } 

    public Identification Id { get; set; } 
} 

現在我需要按年齡對列表進行排序。很簡單

public static void SortByAge(List<Person> listToSort) 
{ 
    listToSort.Sort((x, y) => x.Age.CompareTo(y.Age)); 
} 

即使通過NiNumber和名字是相當簡單的這樣

public static void SortByNiNumber(List<Person> listToSort) 
{ 
     listToSort.Sort((x, y) => x.Id.NiNumber.CompareTo(y.Id.NiNumber)); 
} 

public static void SortByFirstName(List<Person> listToSort) 
{ 
     listToSort.Sort((x, y) => x.Id.Name.FirstName.CompareTo(y.Id.Name.FirstName)); 
} 

現在到了棘手的部分。我需要執行上述所有類型,並給出一個字符串來表示要排序的屬性的路徑。 像「Id.Name.FirstName」

所以我需要

public static void SortByAny(List<Person> listToSort, String sortBy) 
{ 
    //?????? 
} 

,可以用

List<Person> theList = new List<Person>(); 
SortByAny(theList, "Age"); 
SortByAny(theList, "Id.NiNumber"); 
SortByAny(theList, "Id.Name.FirstName"); 

我知道我需要使用反射這個調用,我已成功地這樣做,但我不能超過人類本身的屬性,所以我可能需要做一些其他的事情,這是我卡住的地方。

有沒有人有一些關於如何解決這個問題的精彩想法?

感謝

+0

你可以使用這個解決方案的一部分:http://stackoverflow.com/questions/4473928/c-sharp-dynamic-string-property-path –

+0

你能解釋爲什麼你需要這個?也許有你的問題的另一種解決方案 –

+0

我需要它,因爲我寧願單一排序方法實施排序方法我每個10-100類交換所有排序選項。 當然,這將工作得很好,但給了我很多代碼來維護 – Beaker

回答

0

您可以修改@ E.Mourits鏈接的方法:C# dynamic. String property path。 我加入了一些錯誤檢查,出錯的時候你必須檢查的InvalidOperationException這個Sort方法可以拋出。

static void SortByAny<T>(List<T> list, string path) 
{ 
    list.Sort((x, y) => ReflectOnPath(x, path).CompareTo(ReflectOnPath(y, path))); 
} 

static IComparable ReflectOnPath(object o, string path) 
{ 
    object value = o; 
    var pathComponents = path.Split('.'); 
    foreach (var component in pathComponents) 
    { 
     if (value == null) 
     { 
      throw new NullReferenceException($"Path '{path}' can not be resolved at: {component}."); 
     } 
     var prop = value.GetType().GetProperty(component); 
     if (prop == null) 
     { 
      throw new ArgumentException($"Path '{path}' can not be resolved at: {component}.", nameof(path)); 
     } 
     value = prop.GetValue(value, null); 
    } 
    if (!(value is IComparable)) 
    { 
     throw new ArgumentException($"Value at path '{path}' does not implement ICompareable.", nameof(path)); 
    } 
    return (IComparable)value; 
} 

如果一定要比較的值不執行IComparable你必須增加更多的細節你想怎麼他們在這種情況下進行比較。

+0

哇,工作。 :) – Beaker

+0

只有這些解決方案的問題是性能。 不相信有很多優化與此。 – Beaker

0

因爲通過反射獲取屬性是挺難做的,只是使用和修改這個代碼:

public static void SortByAny(List<Person> listToSort, String sortBy) 
{ 
    if (sortBy == "Email") 
     listToSort.Sort((x, y) => x.Email.CompareTo(y.Email)); 
    else if (sortBy == "Phone") 
     listToSort.Sort((x, y) => x.Phone.CompareTo(y.Phone)); 
    else if (sortBy == "Age") 
     listToSort.Sort((x, y) => x.Age.CompareTo(y.Age)); 
    else if (sortBy == "Id.NiNumber") 
     listToSort.Sort((x, y) => x.Id.NiNumber.CompareTo(y.Id.NiNumber)); 
    else if (sortBy == "Id.Name.FirstName") 
     listToSort.Sort((x, y) => x.Id.Name.FirstName.CompareTo(y.Id.Name.FirstName)); 
    else if (sortBy == "Id.Name.LastName") 
     listToSort.Sort((x, y) => x.Id.Name.LastName.CompareTo(y.Id.Name.LastName)); 
} 
+0

感謝您的建議 這或多或少是我想出來的,但問題在於深入鑽研.. – Beaker

0

你真的需要把反思的地方?如果你需要按照特定和確定數量的「路徑」對列表進行排序,我建議你以固定的方式實施它,直接進行排序。也許交換機可能有幫助?

switch(sortByString){ 
    case: "Id.NiNumber": 
    SortByNiNumber(List<Person> listToSort); 
    break; 
    ... 
} 

如果您沒有太多的選項,它會更快。也許你可以用搜索路徑和委託或操作的字典替換交換機。

+0

我現在就這樣做。但是這必須適用於任何類,而不僅僅是Person,而且我需要它不必爲所有類編寫所有排序表達式... – Beaker

+0

Mmh ...猜猜你應該給出更多的背景。通常,當我必須做一些如此「普遍」的事情時,答案就是改變觀點並重新思考它。 –