2012-06-24 28 views
20

Tôi đang tìm một phương thức để chuyển thuộc tính cho một hàm. Không phải giá trị của tài sản. Hàm không biết trước thuộc tính nào sẽ được sử dụng để sắp xếp. Cách đơn giản nhất trong ví dụ này là: tạo 4 ghi đè với các kiểu tham số khác nhau. Cách khác là sử dụng chức năng bên trong typeof(). Cả hai cách này đều không thể chấp nhận được khi Class1 có hàng trăm thuộc tính. Cho đến nay tôi đã tìm thấy phương pháp sau:Vượt qua thuộc tính để hoạt động như tham số trong 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"); 
} 

Tôi không thích phương pháp này vì có nguy cơ "lỗi của con người" khi chuyển chuỗi tới hàm xử lý. Khi chuỗi được tạo ra bởi một phần khác của mã này sẽ thậm chí còn xấu xí hơn. Xin vui lòng, đề xuất cách thanh lịch hơn để thực hiện việc chuyển thuộc tính Class1 để hoạt động để xử lý thêm. Tùy chọn tốt nhất để sử dụng IMHO sẽ là (hoặc một cái gì đó như thế này):

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

Phiên bản .NET? – alf

+0

Tài sản được sử dụng để sắp xếp được chọn ở địa điểm đầu tiên như thế nào? – arootbeer

+0

Phiên bản .NET 4.0 –

Trả lời

39

Bạn có thể chuyển bộ tiếp cận thuộc tính cho phương thức.

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; 
} 

Bạn sẽ gọi nó là như thế này:

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

Nhưng bạn có thể đi một bước xa hơn và viết phương pháp khuyến nông của riêng bạn.

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; 
    } 
} 

Bây giờ bạn có thể sắp xếp như thế này

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

mà còn

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

Lưu ý rằng tôi tuyên bố tham số đầu tiên như ICollection<T> bởi vì nó phải đáp ứng được hai điều kiện:

  1. Phải có Count thuộc tính
  2. Phải là IEnumerable<T> để có thể áp dụng phương pháp LINQ OrderBy.

List<Class1>ICollection<T> nhưng cũng là một mảng Person[] như nhiều bộ sưu tập khác.

+0

Phương thức này có khác gì so với OrderBy của LINQ không? –

+0

Olivier, cảm ơn bạn rất nhiều! Hoặc merci beaucoup :) –

+0

@ Chris Gessler: Có, nếu tập hợp nguồn là 'null' hoặc' Số' là '0' thì' null' được trả về, ngược lại 'Danh sách ' được trả về. 'OrderBy' của LINQ ném một ngoại lệ nếu nguồn là' null' và trả về một 'IEnumerable ' nếu không. Tôi không biết liệu phương pháp này có thực sự hữu ích cho người khác hay không, nhưng OP cần nó rõ ràng. –

3

Tại sao bạn không sử dụng LINQ cho điều này? Giống như:

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

Các lớp khác sẽ sử dụng 'Processor'. Họ không nên thấy 'xe'. Ngoài ra 'sortBy' sẽ có nhiều logic hơn (tăng dần/giảm dần/lọc) ... –

+0

Sau đó, tôi nghĩ bạn nên xem câu trả lời từ @olivier ở trên, về cách viết phương thức mở rộng của riêng bạn (để thêm bộ lọc, v.v.) – joakimbeng

17

Bạn có thể sử dụng một biểu thức lambda để chuyển thông tin bất động sản:

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"); 
    } 
} 

Cách sử dụng:

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

Bạn có thể sử dụng phương pháp này, nếu bạn cần biết thêm thông tin về một thuộc tính, ví dụ như tên thuộc tính (hữu ích khi triển khai 'INotifyPropertyChanged'). Tuy nhiên, đây là loại overkill trong tình huống này, vì nó là hoàn toàn đủ để trả lại giá trị tài sản. –

0

Chỉ cần thêm từ các câu trả lời ở trên. Bạn cũng có thể làm một lá cờ đơn giản cho hướng đơn đặt hàng.

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

Điều tôi thấy mất tích từ câu trả lời của @ MatthiasG là cách lấy giá trị tài sản không chỉ tên của nó.

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

sử dụng:

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

vĩ đại giải pháp trên đây ...

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"); 
} 
Các vấn đề liên quan