2012-06-24 134 views
20

我正在尋找一種方法將屬性本身傳遞給一個函數。不是財產的價值。函數事先不知道哪些屬性將用於排序。在這個例子中最簡單的方法是:用不同的參數類型創建4個覆蓋。其他方式是使用typeof()裏面的函數。當Class1擁有數百個屬性時,這兩種方式都是不可接受的。到目前爲止,我發現下面的方法:傳遞屬性本身作爲參數在C#

class Class1 
{ 
    string vehName; 
    int maxSpeed; 
    int fuelCapacity; 
    bool isFlying; 
} 

class Processor 
{ 
    List<Class1> vehicles = null; 
    Processor(List<Class1> input) 
    { 
     vehicles = input; 
    } 

    List<Class1> sortBy(List<Class1> toSort, string propName) 
    { 
     if (toSort != null && toSort.Count > 0) 
     { 
      return toSort.OrderBy(x => typeof(Class1).GetProperty(propName).GetValue(x, null)).ToList(); 
     } 
     else return null; 
    } 
} 

class OuterUser 
{ 
    List<Class1> vehicles = new List<Class1>(); 
    // ... fill the list 
    Processor pr = new Processor(vehicles); 
    List<Class1> sorted = pr.sortBy("maxSpeed"); 
} 

傳遞字符串處理函數時,我不喜歡,因爲「人爲失誤」的風險的這種方法。當字符串由其他部分代碼生成時,這會變得更加難看。 請提出更優雅的方式來實現Class1屬性的傳遞以進行進一步處理。使用恕我直言的最佳選擇將是(或類似的東西):

vehicles = sortBy(vehicles, Class1.maxSpeed); 
+2

哪個.NET版本? – alf

+0

首先如何選擇用於排序的屬性? – arootbeer

+0

版本.NET 4.0 –

回答

39

您可以將屬性訪問器傳遞給該方法。

List<Class1> SortBy(List<Class1> toSort, Func<Class1, IComparable> getProp) 
{ 
    if (toSort != null && toSort.Count > 0) { 
     return toSort 
      .OrderBy(x => getProp(x)) 
      .ToList(); 
    } 
    return null; 
} 

你會這樣稱呼它:

var result = SortBy(toSort, x => x.maxSpeed); 

但是你可以走一步,寫自己的擴展方法。

public static class CollectionExtensions 
{ 
    public static List<TSource> OrderByAsListOrNull<TSource, TKey>(
     this ICollection<TSource> collection, Func<TSource,TKey> keySelector) 

     if (collection != null && collection.Count > 0) { 
      return collection 
       .OrderBy(x => keySelector(x)) 
       .ToList(); 
     } 
     return null; 
    } 
} 

現在您可以排序像這樣

List<Class1> sorted = toSort.OrderByAsListOrNull(x => x.maxSpeed); 

而且,我宣佈第一個參數爲ICollection<T>

Person[] people = ...; 
List<Person> sortedPeople = people.OrderByAsListOrNull(p => p.LastName); 

注意,因爲它必須滿足兩個條件:

  1. 它必須有一個Count財產
  2. 它必須是IEnumerable<T>爲了能夠應用LINQ方法OrderBy

List<Class1>是一個ICollection<T>,但也是一個數組Person[]許多其他集合。

+0

此方法與LINQ的OrderBy有什麼不同嗎? –

+0

Olivier,非常感謝!或者merci beaucoup :) –

+0

@Chris Gessler:是的,如果源集合是'null'或者它的'Count'是'0',則返回'null',否則返回'List '。如果源爲'null',LINQ的'OrderBy'將拋出異常,否則返回'IEnumerable '。我不知道這種方法對其他人是否真的有用,但OP顯然需要它。 –

3

你爲什麼不使用Linq的呢?像:

vehicles.OrderBy(v => v.maxSpeed).ToList(); 
+0

其他類將使用'Processor'。他們不應該看到「車輛」。另外'sortBy'會有更多的邏輯(升序/降序/某些過濾)... –

+0

然後我想你應該看看上面@olivier的回答,關於編寫你自己的擴展方法(添加過濾等) – joakimbeng

17

您可以使用lambda表達式來傳遞屬性信息:

void DoSomething<T>(Expression<Func<T>> property) 
{ 
    var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo; 
    if (propertyInfo == null) 
    { 
     throw new ArgumentException("The lambda expression 'property' should point to a valid Property"); 
    } 
} 

用法:

DoSomething(() => this.MyProperty); 
+0

如果您需要獲取有關屬性的更多信息(如實例的屬性名稱(在實現INotifyPropertyChanged時很有用)),則可以使用此方法。然而,在這種情況下,這是一種矯枉過正的情況,因爲它完全足以返回財產價值。 –

0

只是從上面的答案補充。您也可以爲訂單方向做一個簡單的標誌。

public class Processor 
{ 
    public List<SortableItem> SortableItems { get; set; } 

    public Processor() 
    { 
     SortableItems = new List<SortableItem>(); 
     SortableItems.Add(new SortableItem { PropA = "b" }); 
     SortableItems.Add(new SortableItem { PropA = "a" }); 
     SortableItems.Add(new SortableItem { PropA = "c" }); 
    } 

    public void SortItems(Func<SortableItem, IComparable> keySelector, bool isAscending) 
    { 
     if(isAscending) 
      SortableItems = SortableItems.OrderBy(keySelector).ToList(); 
     else 
      SortableItems = SortableItems.OrderByDescending(keySelector).ToList(); 
    } 
} 
3

我發現從@ MatthiasG的答案缺少的是如何獲取屬性值不只是它的名字。

public static string Meth<T>(Expression<Func<T>> expression) 
{ 
    var name = ((MemberExpression)expression.Body).Member.Name; 
    var value = expression.Compile()(); 
    return string.Format("{0} - {1}", name, value); 
} 

使用:在這裏

Meth(() => YourObject.Property); 
2

的最佳解決方案......

Passing properties by reference in C#

void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr) 
{ 
    if (!string.IsNullOrEmpty(input)) 
    { 
     var expr = (MemberExpression) outExpr.Body; 
     var prop = (PropertyInfo) expr.Member; 
     prop.SetValue(target, input, null); 
    } 
} 

void Main() 
{ 
    var person = new Person(); 
    GetString("test", person, x => x.Name); 
    Debug.Assert(person.Name == "test"); 
}