6
votes

Union de deux objets basés sur l'égalité de leurs membres

Laissez-nous commencer par une définition de classe pour l'exemple de l'exemple: xxx pré>

supposons maintenant que j'ai une liste code> appelée personnes contenant 3 objets: p>

string[] members = new string[] { "FirstName", "LastName", "Age", "Grade" };
foreach (string member in members)
{
    if (people.Select(p => p.**member**).Distinct().Count() == 1)
        unionMan.**member** = people[0].**member**;
}

c#

1 commentaires

Eh bien, l'idée de votre seconde devrait fonctionner, mais vraiment pas avec le code indiqué! À propos, vous devez avoir un constructeur par défaut pour la personne avec vos valeurs "non valides".


3 Réponses :


2
votes

Votre dernière idée me semble bon, quelque chose comme ceci: xxx

quelques observations:

  • Les membres de votre classe doivent être définis comme des propriétés et non des membres publics et les deux get et set accessor doivent être publics
  • Le constructeur par défaut doit définir les valeurs "non valides" (comme suggérée correctement par @ Raphaà «Lalthaus)

    Donc, la personne de la classe ressemblerait à ceci: xxx


3 commentaires

Excellent point sur le constructeur par défaut. Y a-t-il une raison d'utiliser PropertyInfo.GetGetMethod () et PropertyInfo.GetSetMethod () Over PropertyInfo.getvalue () et PropriétéInfo. SETVALUE () ? Sinon, je pense que ce dernier est un peu moins verbeux.


@Jonsenchyna: J'ai simplement oublié ces shourtuts: p merci pour l'indice;)


Ah oui, c'est simplement un exemple de classe que j'ai jeté ensemble dans l'éditeur de la présente, l'objet réel a en effet un constructeur qui définit chaque membre à son état "invalide" par défaut. En ce qui concerne les propriétés VS membres, l'objet réel que je travaille est défini dans une assemblée externe que je ne puisse pas changer en raison de problèmes de dépendance.



1
votes

mise à jour: strong> puisque vous n'avez pas de contrôle sur la classe de la personne et que l'état est défini dans les champs publics, plutôt que dans les propriétés, j'ai mis à jour la solution pour résoudre ce problème.

Je voudrais recommande d'utiliser la réflexion. Vous voudriez obtenir le Fieldinfo (ou PropertyInfo ) Objet à l'avance, plutôt que de l'obtenir Pour chaque entrée dans votre requête LINQ. Vous pouvez les obtenir en utilisant Type.getfield et type.geproperty . Une fois que vous avez ceci, vous pouvez simplement utiliser Fieldinfo A> / PropertyInfo.gevalue et fieldinfo / PropertyInfo.setvalue . p>

Par exemple: P>

Type personType = typeof(Person);
foreach(string member in members)
{   // Get Fields via Reflection
    FieldInfo field = peopleType.GetField(member);
    if(field != null)
    {
        if (people.Select(p => field.GetValue(p, null) ).Distinct().Count() == 1)
        {
            field.SetValue(unionMan, field.GetValue(people[0], null), null);
        }
    }
    else // If member is not a field, check if it's a property instead
    {   // Get Properties via Reflection
        PropertyInfo prop = peopleType.GetProperty(member);
        if(prop != null)
        {
            if (people.Select(p => prop.GetValue(p, null) ).Distinct().Count() == 1)
            {
                prop.SetValue(unionMan, prop.GetValue(people[0], null), null);
            }
        }
    }
}


1 commentaires

Ce n'est en effet qu'un sous-ensemble des propriétés. En ce qui concerne les valeurs non valides, cela se fait déjà dans le constructeur alors que je l'ai signalé dans mon commentaire à Digemall il y a des moments. J'aime votre recommandation de stocker la propriétéInfo cependant, merci!



5
votes

Il est inefficace de faire une distinction de toutes les valeurs simplement pour compter les membres distincts. Vous avez un scénario de raccourci dans lequel la recherche d'une valeur dans tout em> des éléments suivants n'a pas la même valeur que le premier élément de l'élément signifie que vous avez un état invalide pour cette colonne.

quelque chose comme Cela devrait fonctionner, bien que davantage de travaux devraient être effectués si l'un des membres est des matrices, une évaluation récursive ou une autre logique plus complexe (note que je n'ai pas testé cela): P>

public static T UnionCombine<T>(this IEnumerable<T> values) where T : new() {
    var newItem = new T();
    var properties = typeof(T).GetProperties();
    for (var prop in properties) {
        var pValueFirst = prop.GetValue(values.First(), null);
        var useDefaultValue = values.Skip(1).Any(v=>!(Object.Equals(pValueFirst, prop.GetValue(v, null))));
        if (!useDefaultValue) prop.SetValue(newItem, pValueFirst, null);
    }
    return newItem;
}


3 commentaires

+1 Super idée de ne pas utiliser distinct mais juste vérifier sur la première valeur :)


+1 très élégant. Vous avez juste oublié d'ajouter le mot ceci à votre déclaration de paramètre.


Pas de tableaux, Seulement des types de données simples int et string . +1 pour l'observation que distincte () n'est pas nécessaire dans ce cas.