web-dev-qa-db-fra.com

Passer la propriété elle-même pour fonctionner comme paramètre en C #

Je cherche une méthode pour passer la propriété elle-même à une fonction. Pas la valeur de la propriété. La fonction ne sait pas à l'avance quelle propriété sera utilisée pour le tri. La façon la plus simple dans cet exemple est de créer 4 écrasements avec différents types de paramètres. L'autre manière est d'utiliser typeof() à l'intérieur de la fonction. Ces deux façons sont inacceptables lorsque Class1 a des centaines de propriétés. Jusqu'à présent, j'ai trouvé la méthode suivante:

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

Je n'aime pas cette méthode en raison du risque d '"erreur humaine" lors du passage de la chaîne à la fonction de traitement. Lorsque la chaîne est générée par une autre partie du code, cela devient encore plus laid. Veuillez suggérer un moyen plus élégant d'implémenter le passage de la propriété Class1 pour fonctionner pour un traitement ultérieur. La meilleure option pour l'utilisation à mon humble avis sera (ou quelque chose comme ça):

vehicles = sortBy(vehicles, Class1.maxSpeed);
38
Ivy Growing

Vous pouvez transmettre un accesseur de propriété à la méthode.

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

Vous l'appeleriez ainsi:

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

Mais vous pouvez aller plus loin et écrire votre propre méthode d'extension.

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

Maintenant, vous pouvez trier comme ceci

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

mais aussi

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

Notez que j'ai déclaré le premier paramètre comme ICollection<T> car il doit remplir deux conditions:

  1. Il doit avoir une propriété Count
  2. Ce doit être un IEnumerable<T> afin de pouvoir appliquer la méthode LINQ OrderBy.

List<Class1> est un ICollection<T> mais aussi un tableau Person[] comme beaucoup d'autres collections.


Jusqu'à présent, j'ai montré comment lire une propriété. Si la méthode doit définir une propriété, vous devez également lui transmettre un délégué setter

void ReadAndWriteProperty(Func<Class1, T> getProp, Action<Class1, T> setProp)

T est le type de la propriété.

58

Vous pouvez utiliser une expression lambda pour transmettre des informations sur les propriétés:

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

Usage:

DoSomething(() => this.MyProperty);
27
MatthiasG

Ce que j'ai trouvé manquant dans la réponse de @ MatthiasG, c'est comment obtenir la valeur de la propriété et pas seulement son nom.

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

utilisation:

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

Excellente solution ici ...

Passage des propriétés par référence en 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");
}
4
Carter Medlin

Pourquoi n'utilisez-vous pas Linq pour cela? Comme:

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

Juste pour ajouter des réponses ci-dessus. Vous pouvez également faire un simple indicateur pour la direction de la commande.

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();
    }
}
0
cubski