2010-12-02 3 views
13

En bref: j'ai 2 collections d'objets. L'un contient de bonnes valeurs (appelons-le "Bon"), les autres valeurs par défaut ("Default"). Je veux l'Intersection de l'Union entre le bien et le défaut, et le défaut. En d'autres termes: Intersect (Union (Good, Default), Default). On pourrait penser qu'il se résout en Default, mais voici où cela devient difficile: J'utilise un IEqualityComparer personnalisé.Intersection avec un IEqualityComparer personnalisé à l'aide de Linq

je suis arrivé les classes suivantes:

class MyClass 
{ 
    public string MyString1; 
    public string MyString2; 
    public string MyString3; 
} 

class MyEqualityComparer : IEqualityComparer<MyClass> 
{ 
    public bool Equals(MyClass item1, MyClass item2) 
    { 
     if(item1 == null && item2 == null) 
      return true; 
     else if((item1 != null && item2 == null) || 
       (item1 == null && item2 != null)) 
      return false; 

     return item1.MyString1.Equals(item2.MyString1) && 
       item1.MyString2.Equals(item2.MyString2); 
    } 

    public int GetHashCode(MyClass item) 
    { 
     return new { item.MyString1, item.MyString2 }.GetHashCode(); 
    } 
} 

Voici les caractéristiques de mes collections bonnes et collections par défaut:

Par défaut: Il est un grand ensemble, contenant toutes les paires voulaient {de LaChaine1, LaChaine2} , mais les valeurs MyString3 sont, comme vous pouvez le deviner, les valeurs par défaut. Bon: C'est un ensemble plus petit, contenant principalement des éléments qui sont dans le jeu par défaut, mais avec de bonnes valeurs MyString3. Il a aussi {MyString1, MyString2} qui sont en dehors de l'ensemble voulu. Ce que je veux faire est ceci: Ne prenez que les éléments de Good qui sont dans Default, mais ajoutez les autres éléments dans Default à cela.

Voici, ce que je pense est, mieux essayer mon:

HalfWantedResult = Good.Union(Default, new MyEqualityComparer()); 
WantedResult= HalfWantedResult.Intersect(Good, new MyEqualityComparer()); 

je l'ai enseigné aurait travaillé, mais le résultat que je reçois est fondamentalement que le bien {LaChaine1, LaChaine2} paires définies, mais tous provenant de l'ensemble par défaut, donc j'ai la valeur par défaut tout au long. J'ai aussi essayé de changer le Default et le Good du dernier Intersect, mais j'ai le même résultat.

+3

Votre Equals mise en œuvre est vraiment mauvais.Il y aura des collisions de hachage qui ne devraient pas être là. Pourquoi ne pas utiliser la même projection ('new {item.MyString1, item.MyString2}') mais appeler Equals? –

+0

Je devrais regarder dans ceci, cela pourrait faire partie du problème. L'Union utilise GetHashCode et Intersects utilise les Equals. Je n'ai pas vraiment enseigné dans cette partie. * honte * – Tipx

Répondre

18

d'abord tout cela est faux:

public bool Equals(MyClass item1, MyClass item2) 
{ 
    return GetHashCode(item1) == GetHashCode(item2); 
} 

Si les années hashcode sont différentes pour que les 2 éléments correspondants sont différents, mais si elles sont égales est pas garanti que les 2 articles correspondants sont égaux.

C'est donc la mise en œuvre Equals correcte:

public bool Equals(MyClass item1, MyClass item2) 
{ 
    if(object.ReferenceEquals(item1, item2)) 
     return true; 
    if(item1 == null || item2 == null) 
     return false; 
    return item1.MyString1.Equals(item2.MyString1) && 
      item1.MyString2.Equals(item2.MyString2); 
} 

Comme Slacks suggested (me anticiper) le code est le suivant:

var Default = new List<MyClass> 
{ 
    new MyClass{MyString1="A",MyString2="A",MyString3="-"}, 
    new MyClass{MyString1="B",MyString2="B",MyString3="-"}, 
    new MyClass{MyString1="X",MyString2="X",MyString3="-"}, 
    new MyClass{MyString1="Y",MyString2="Y",MyString3="-"}, 
    new MyClass{MyString1="Z",MyString2="Z",MyString3="-"}, 

}; 
var Good = new List<MyClass> 
{ 
    new MyClass{MyString1="A",MyString2="A",MyString3="+"}, 
    new MyClass{MyString1="B",MyString2="B",MyString3="+"}, 
    new MyClass{MyString1="C",MyString2="C",MyString3="+"}, 
    new MyClass{MyString1="D",MyString2="D",MyString3="+"}, 
    new MyClass{MyString1="E",MyString2="E",MyString3="+"}, 
}; 
var wantedResult = Good.Intersect(Default, new MyEqualityComparer()) 
         .Union(Default, new MyEqualityComparer()); 

// wantedResult: 
// A A + 
// B B + 
// X X - 
// Y Y - 
// Z Z - 
+0

+1 pour m'avoir beaucoup aidé avec mes égales, mais pas accepté car cela ne résout pas le problème. (Je souhaite que je pourrais +2! :-P) – Tipx

+0

Aucun prob. J'écrivais la bonne solution pour votre problème d'intersection, mais Slacks m'avait anticipé. Son est la réponse ;-) – digEmAll

+0

Vérifiez mon édition, Il montre Slaks est correct – digEmAll

10

Vous devez vérifier l'égalité réelle, non seulement Hashcode l'égalité .

GetHashCode() n'est pas (et ne peut pas être) sans collision, c'est pourquoi la méthode Equals est nécessaire en premier lieu.

En outre, vous pouvez faire beaucoup plus simplement en écrivant

WantedResult = Good.Concat(Default).Distinct(); 

La méthode Distinct retourne le premier élément de chaque paire de doublons, donc cela renverra le résultat souhaité.

EDIT: Cela devrait être

WantedResult = Good.Intersect(Default, new MyEqualityComparer()) 
        .Union(Default, new MyEqualityComparer()); 
+0

Si j'utilise Concat & Distinct, j'aurai le résultat que je veux, mais je vais aussi avoir quelques items du bon set qui n'étaient pas dans la portée voulue (Défaut). – Tipx

+0

@Tip: Voir mes modifications. – SLaks

+0

Les opérations Union et Intersection sont commutatives, donc cela ne change pas le résultat, mais oui, cela évite d'utiliser une variable intermédiaire. – Tipx

Questions connexes