J’aimerais avoir un retour sur la meilleure façon d’écrire une fonction générique permettant de comparer deux listes. Les listes contiennent des objects de classe et nous souhaitons parcourir une liste en recherchant le même élément dans une seconde liste et en signalant toute différence.
Nous avons déjà une méthode pour comparer les classes, nous avons donc besoin de commentaires sur la façon dont nous pouvons alimenter la méthode (illustrée ci-dessous) à partir de deux listes.
Par exemple, supposons que nous ayons une classe simple “Employé” qui possède trois propriétés, Nom, ID, Département. Nous voulons signaler les différences entre List et une autre liste.
Remarque:
Les deux listes contiennent toujours le même nombre d’éléments.
Comme mentionné ci-dessus, nous avons une méthode générique que nous utilisons pour comparer deux classes, comment pouvons-nous incorporer cette méthode pour répondre aux listes, à partir d’une autre méthode, parcourir la liste et alimenter les classes avec la méthode générique …. comment trouver la classe équivalente dans la deuxième liste pour passer à la méthode ci-dessous;
public static ssortingng CompareTwoClass_ReturnDifferences(T1 Orig, T2 Dest) where T1 : class where T2 : class { // Instantiate if necessary if (Dest == null) throw new ArgumentNullException("Dest", "Destination class must first be instantiated."); var Differences = CoreFormat.SsortingngNoCharacters; // Loop through each property in the destination foreach (var DestProp in Dest.GetType().GetProperties()) { // Find the matching property in the Orig class and compare foreach (var OrigProp in Orig.GetType().GetProperties()) { if (OrigProp.Name != DestProp.Name || OrigProp.PropertyType != DestProp.PropertyType) continue; if (OrigProp.GetValue(Orig, null).ToSsortingng() != DestProp.GetValue(Dest, null).ToSsortingng()) Differences = Differences == CoreFormat.SsortingngNoCharacters ? ssortingng.Format("{0}: {1} -> {2}", OrigProp.Name, OrigProp.GetValue(Orig, null), DestProp.GetValue(Dest, null)) : ssortingng.Format("{0} {1}{2}: {3} -> {4}", Differences, Environment.NewLine, OrigProp.Name, OrigProp.GetValue(Orig, null), DestProp.GetValue(Dest, null)); } } return Differences; }
Des suggestions ou des idées appréciées?
Edit: Ciblage de .NET 2.0 pour que LINQ soit hors de question.
…. mais comment trouver la classe équivalente dans la deuxième liste pour passer à la méthode ci-dessous;
Ceci est votre problème actuel; vous devez avoir au moins une propriété immuable, un identifiant ou quelque chose du genre, pour identifier les objects correspondants dans les deux listes. Si vous ne possédez pas une telle propriété, vous ne pouvez pas résoudre le problème sans erreurs. Vous pouvez simplement essayer de deviner les objects correspondants en recherchant des modifications minimales ou logiques.
Si vous avez une telle propriété, la solution devient vraiment simple.
Enumerable.Join( listA, listB, a => a.Id, b => b.Id, (a, b) => CompareTwoClass_ReturnDifferences(a, b))
merci à vous deux danbruc et noldorin pour vos commentaires. les deux listes auront la même longueur et dans le même ordre. donc la méthode ci-dessus est proche, mais pouvez-vous modifier cette méthode pour passer le enum.Current à la méthode que j’ai posté ci-dessus?
Maintenant je suis confus … quel est le problème avec ça? Pourquoi pas juste ce qui suit?
for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++) { yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]); }
L'appel Math.Min () peut même être omis si une longueur égale est garantie.
L'implémentation de Noldorin est bien sûr plus intelligente à cause du délégué et de l'utilisation des énumérateurs au lieu d'utiliser ICollection.
Cette solution produit une liste de résultats contenant toutes les différences entre les deux listes d’entrée. Vous pouvez comparer vos objects par n’importe quelle propriété, dans mon exemple, il s’agit de l’ID. La seule ressortingction est que les listes doivent être du même type:
var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id)) .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));
Je pense que vous cherchez une méthode comme celle-ci:
public static IEnumerable CompareSequences(IEnumerable seq1, IEnumerable seq2, Func comparer) { var enum1 = seq1.GetEnumerator(); var enum2 = seq2.GetEnumerator(); while (enum1.MoveNext() && enum2.MoveNext()) { yield return comparer(enum1.Current, enum2.Current); } }
Ce n’est pas testé, mais ça devrait quand même faire le travail. Notez que ce qui est particulièrement utile à propos de cette méthode est qu’elle est générique, c’est-à-dire qu’elle peut prendre deux séquences de types arbitraires (et différents) et renvoyer des objects de tout type.
Cette solution suppose bien sûr que vous vouliez comparer le nième élément de seq1
avec le nième élément de seq2
. Si vous voulez faire correspondre les éléments des deux séquences en fonction d’une propriété / comparaison particulière, vous devrez alors effectuer une opération de jointure (comme suggéré par danbruc en utilisant Enumerable.Join
. Faites-le moi savoir si aucun des deux ces approches sont tout à fait ce que je recherche et je peux peut-être suggérer autre chose.
Modifier: voici un exemple d’utilisation de la méthode CompareSequences
avec la fonction de CompareSequences
que vous avez initialement publiée.
// Prints out to the console all the results returned by the comparer function (CompareTwoClass_ReturnDifferences in this case). var results = CompareSequences(list1, list2, CompareTwoClass_ReturnDifferences); int index; foreach(var element in results) { Console.WriteLine("{0:#000} {1}", index++, element.ToSsortingng()); }
Cette approche de Microsoft fonctionne très bien et offre la possibilité de comparer une liste à une autre et de les modifier pour obtenir la différence. Si vous comparez des classes, ajoutez simplement vos objects à deux listes distinctes, puis exécutez la comparaison.
J’espère que je comprends bien votre question, mais vous pouvez le faire très rapidement avec Linq. Je suppose que vous aurez toujours une propriété Id. Créez simplement une interface pour vous en assurer.
Si vous identifiez un object avec les mêmes modifications d’une classe à l’autre, je vous recommande de transmettre un délégué qui renvoie true si les deux objects ont le même identifiant persistant.
Voici comment le faire dans Linq:
List listA = new List (); List listB = new List (); listA.Add(new Employee() { Id = 1, Name = "Bill" }); listA.Add(new Employee() { Id = 2, Name = "Ted" }); listB.Add(new Employee() { Id = 1, Name = "Bill Sr." }); listB.Add(new Employee() { Id = 3, Name = "Jim" }); var identicalQuery = from employeeA in listA join employeeB in listB on employeeA.Id equals employeeB.Id select new { EmployeeA = employeeA, EmployeeB = employeeB }; foreach (var queryResult in identicalQuery) { Console.WriteLine(queryResult.EmployeeA.Name); Console.WriteLine(queryResult.EmployeeB.Name); }