2012-06-24 22 views
20

Estoy buscando un método para pasar la propiedad a una función. No es el valor de la propiedad. La función no sabe de antemano qué propiedad se utilizará para la clasificación. La forma más simple en este ejemplo es: crear 4 sobrescrituras con diferentes tipos de parámetros. Otra forma es usando typeof() dentro de la función. Ambas formas son inaceptables cuando Class1 tiene cientos de propiedades. Hasta ahora encontré el siguiente método:Pase la propiedad en sí para funcionar como parámetro en 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"); 
} 

No me gusta este método debido al riesgo de "error humano" al pasar la cadena a la función de procesamiento. Cuando la cadena es generada por otra parte del código, esto será aún más feo. Por favor, sugiera una forma más elegante de implementar el paso de la propiedad de Clase 1 para funcionar para un procesamiento posterior. La mejor opción para el uso IMHO será (o algo como esto):

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

Qué versión .NET? – alf

+0

¿Cómo se usa la propiedad que se utilizará para la selección seleccionada en primer lugar? – arootbeer

+0

versión .NET 4.0 –

Respuesta

39

Puede pasar un descriptor de acceso a la propiedad al método.

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

Se podría llamar así:

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

Pero usted podría ir un paso más allá y escribir su propio método de extensió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; 
    } 
} 

Ahora se puede clasificar como éste

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

sino también

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

Tenga en cuenta que he declarado el primer parámetro como ICollection<T> porque debe cumplir dos condiciones:

  1. Debe tener un Count propiedad
  2. Debe ser IEnumerable<T> para poder aplicar el método LINQ OrderBy.

List<Class1> es un ICollection<T> sino también una matriz Person[] como muchas otras colecciones.

+0

¿Este método hace algo diferente al pedido de LINQ? –

+0

Olivier, ¡muchas gracias! O merci beaucoup :) –

+0

@Chris Gessler: Sí, si la colección de origen es 'null' o su' Count' es '0', se devuelve' null'; de lo contrario, se devuelve 'List '. 'OrderBy' de LINQ arroja una excepción si el origen es' nulo' y devuelve un 'IEnumerable ' de lo contrario. No sé si este método es realmente útil para otros, pero aparentemente el OP lo necesita. –

3

¿Por qué no utiliza Linq para esto? Me gusta:

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

Otras clases usarán 'Procesador'. No deberían ver 'vehículos'. Además, 'sortBy' tendrá más lógica (ascendente/descendente/algo de filtrado) ... –

+0

Entonces, creo que deberías ver la respuesta de @olivier anterior, sobre cómo escribir tu propio método de extensión (para agregar filtros, etc.) – joakimbeng

17

Puede utilizar una expresión lambda para pasar información de propiedad:

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

Uso:

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

Puede usar este enfoque, si necesita obtener más información sobre una propiedad, como el nombre de la propiedad, por ejemplo (útil al implementar 'INotifyPropertyChanged'). Sin embargo, esto es un exceso en esta situación, ya que es totalmente suficiente para devolver el valor de la propiedad. –

0

sólo para añadir a las respuestas anteriores. También puede hacer una bandera simple para la dirección del pedido.

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

lo que he encontrado que falta de respuesta de @ MatthiasG es cómo conseguir valor de la propiedad no sólo su nombre.

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

uso:

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

Gran solución aquí ...

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