Passa la proprietà stessa a funzionare come parametro in C #

Sto cercando un metodo per passare la proprietà stessa a una funzione. Non valore della proprietà. La funzione non sa in anticipo quale proprietà verrà utilizzata per l’ordinamento. Il modo più semplice in questo esempio è la creazione di 4 sovrascritture con diversi tipi di parametri. L’altro modo è l’uso di typeof() all’interno della funzione. Entrambi questi modi sono inaccettabili quando Class1 ha centinaia di proprietà. Finora ho trovato il seguente metodo:

 class Class1 { string vehName; int maxSpeed; int fuelCapacity; bool isFlying; } class Processor { List vehicles = null; Processor(List input) { vehicles = input; } List sortBy(List 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 vehicles = new List(); // ... fill the list Processor pr = new Processor(vehicles); List sorted = pr.sortBy("maxSpeed"); } 

Non mi piace questo metodo a causa del rischio di “errore umano” durante il passaggio della stringa alla funzione di elaborazione. Quando la stringa viene generata da un’altra parte del codice, questo diventa ancora più brutto. Per favore, suggerisci un modo più elegante per implementare il passaggio della proprietà Class1 per funzionare per ulteriori elaborazioni. La migliore opzione per l’utilizzo di IMHO sarà (o qualcosa del genere):

 vehicles = sortBy(vehicles, Class1.maxSpeed); 

È ansible passare una proprietà accessor al metodo.

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

Lo chiameresti in questo modo:

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

Ma potresti fare un ulteriore passo avanti e scrivere il tuo metodo di estensione.

 public static class CollectionExtensions { public static List OrderByAsListOrNull( this ICollection collection, Func keySelector) if (collection != null && collection.Count > 0) { return collection .OrderBy(x => keySelector(x)) .ToList(); } return null; } } 

Ora puoi ordinare in questo modo

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

ma anche

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

Nota che ho dichiarato il primo parametro come ICollection perché deve soddisfare due condizioni:

  1. Deve avere una proprietà Count
  2. Deve essere un IEnumerable per poter applicare il metodo LINQ OrderBy .

List è un ICollection ma anche un array Person[] come molte altre raccolte.


Finora, ho mostrato come puoi leggere una proprietà. Se il metodo deve impostare una proprietà, è necessario passarlo anche a un delegato setter

 void ReadAndWriteProperty(Func getProp, Action setProp) 

Dove T è il tipo di proprietà.

È ansible utilizzare un’espressione lambda per passare informazioni sulle proprietà:

 void DoSomething(Expression> 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"); } } 

Uso:

 DoSomething(() => this.MyProperty); 

Quello che ho trovato mancante nella risposta di @ MatthiasG è come ottenere il valore della proprietà non solo il suo nome.

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

uso:

 Meth(() => YourObject.Property); 

Perché non usi Linq per questo? Piace:

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

Ottima soluzione qui …

Passando proprietà per riferimento in C #

 void GetString(string input, T target, Expression> 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"); } 

Solo per aggiungere le risposte sopra. Puoi anche fare una semplice bandiera per la direzione dell’ordine.

 public class Processor { public List SortableItems { get; set; } public Processor() { SortableItems = new List(); SortableItems.Add(new SortableItem { PropA = "b" }); SortableItems.Add(new SortableItem { PropA = "a" }); SortableItems.Add(new SortableItem { PropA = "c" }); } public void SortItems(Func keySelector, bool isAscending) { if(isAscending) SortableItems = SortableItems.OrderBy(keySelector).ToList(); else SortableItems = SortableItems.OrderByDescending(keySelector).ToList(); } }