2012-06-24 14 views
20

Szukam metody przekazania właściwości do funkcji. Nie wartość nieruchomości. Funkcja nie wie z góry, która właściwość będzie używana do sortowania. Najprostszym sposobem w tym przykładzie jest: tworzenie 4 nadpisań z różnymi typami parametrów. Innym sposobem jest użycie funkcji wewnętrznej typeof(). Oba te sposoby są niedopuszczalne, gdy klasa 1 ma setki właściwości. Do tej pory znalazłem następującą metodę:Właściwość Pass sama do funkcji jako parametr w 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"); 
} 

Nie podoba mi się ta metoda ze względu na ryzyko "ludzkiego błędu" podczas przekazywania ciągu do funkcji przetwarzania. Kiedy łańcuch zostanie wygenerowany przez inną część kodu, będzie to jeszcze bardziej brzydkie. Proszę, zasugeruj bardziej elegancki sposób wdrożenia przejścia własności Klasy 1 do działania w celu dalszego przetwarzania. Najlepszą opcją użycia IMHO będzie (lub coś w tym stylu):

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

Która wersja .NET? – alf

+0

W jaki sposób właściwość służy do sortowania wybranego w pierwszej kolejności? – arootbeer

+0

wersja .NET 4.0 –

Odpowiedz

39

Możesz przekazać właściwość do metody.

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

Można by nazwać tak:

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

Ale można pójść o krok dalej i napisać własną metodę rozszerzenia.

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

Teraz można sortować like this

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

ale również

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

pamiętać, że ogłoszony pierwszy parametr jako ICollection<T> ponieważ musi spełniać dwa warunki:

  1. Musi mieć numer Count Właściwość
  2. Musi to być IEnumerable<T>, aby móc zastosować metodę LINQ OrderBy.

List<Class1> jest ICollection<T> ale również szereg Person[] jak wiele innych kolekcjach.

+0

Czy ta metoda robi coś innego niż OrderBy LINQ? –

+0

Olivier, dziękuję bardzo! Lub merci beaucoup :) –

+0

@Chris Gessler: Tak, jeśli zbiorem źródłowym jest 'null' lub jego' Count' to '0', to zwracana jest' null', w przeciwnym razie zwracana jest 'List '. LINQ 'OrderBy' zgłasza wyjątek, jeśli źródłem jest' null' i zwraca 'IEnumerable ' w przeciwnym razie. Nie wiem, czy ta metoda jest naprawdę przydatna dla innych, ale PO najwyraźniej jej potrzebuje. –

3

Dlaczego nie używasz Linq do tego? Na przykład:

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

Inne klasy będą używać 'Procesora'. Nie powinni widzieć "pojazdów". Ponadto "sortBy" będzie miał więcej logiki (rosnąco/malejąco/niektóre filtrowanie) ... –

+0

Następnie myślę, że powinieneś spojrzeć na odpowiedź z @olivier powyżej, o pisanie własnej metody rozszerzenia (aby dodać filtrowanie itp.) – joakimbeng

17

Można użyć wyrażenia lambda do przekazywania informacji nieruchomości:

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

Zastosowanie:

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

Możesz użyć tego podejścia, jeśli potrzebujesz uzyskać więcej informacji o właściwości, na przykład nazwę właściwości (przydatne przy implementacji 'INotifyPropertyChanged'). Jest to jednak rodzaj przesady w tej sytuacji, ponieważ wystarczy w pełni zwrócić wartość nieruchomości. –

0

Wystarczy dodać z powyższych odpowiedzi. Możesz również zrobić prostą flagę dla kierunku zamówienia.

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

To, co znalazłem w odpowiedzi @ MatthiasG, to jak uzyskać wartość nieruchomości, a nie tylko jej nazwę.

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

zastosowanie:

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

Świetne rozwiązanie tutaj ...

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"); 
} 
Powiązane problemy