2009-08-24 9 views
2

J'essaie d'utiliser la méthode except avec un comparateur d'égalité personnalisé, mais ce n'est pas un travail.Enumerable.Except ne pas utiliser mon comparateur personnalisé

Mon comparateur d'égalité:

public class BusinessObjectGuidEqualityComparer<T> : IEqualityComparer<T> where T : BusinessObject 
{ 
    #region IEqualityComparer<T> Members 

    /// <summary> 
    /// Determines whether the specified objects are equal. 
    /// </summary> 
    /// <param name="x">The first object of type <paramref name="T"/> to compare.</param> 
    /// <param name="y">The second object of type <paramref name="T"/> to compare.</param> 
    /// <returns> 
    /// <see langword="true"/> If the specified objects are equal; otherwise, <see langword="false"/>. 
    /// </returns> 
    public bool Equals(T x, T y) 
    { 
     return (x == null && y == null) || (x != null && y != null && x.Guid.Equals(y.Guid)); 
    } 

    /// <summary> 
    /// Returns a hash code for this instance. 
    /// </summary> 
    /// <param name="obj">The object to get the hash code.</param> 
    /// <returns> 
    /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
    /// </returns> 
    /// <exception cref="T:System.ArgumentNullException"> 
    /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null. 
    /// </exception> 
    public int GetHashCode(T obj) 
    { 
     if (obj == null) 
     { 
      throw new ArgumentNullException("obj"); 
     } 

     return obj.GetHashCode(); 
    } 

    #endregion 
} 

Mon sauf utilisation:

BusinessObjectGuidEqualityComparer<Area> comparer = new BusinessObjectGuidEqualityComparer<Area>(); 
IEnumerable<Area> toRemove = this.Areas.Except(allocatedAreas, comparer); 
IEnumerable<Area> toAdd = allocatedAreas.Except(this.Areas, comparer); 

La chose étrange est, événement je donne mon comparateur d'égalité personnalisée par défaut un est utilisé, alors qu'est-ce que je fais de mal ?

Merci pour votre aide.

+0

Re votre commentaire; Je vais mettre à jour mon exemple pour montrer mon banc d'essai, qui fonctionne ... –

+0

(juste un côté - le problème éventuel montre l'importance de fournir un exemple de code * complet * ...) –

Répondre

4

Semblable à Marc Je viens de tester cela, tout est appelé très bien, je suppose que vous êtes attrapé par l'exécution différée LINQ, notez le ToArray dans mon code.

Notez que lors du traçage, j'ai remarqué que GetHashCode n'est jamais appelé sur les objets NULL dans le comparateur.

Gardez à l'esprit, MiscUtil a un moyen génial pour vous de le faire en ligne de vêtements, voir: Can I specify my explicit type comparator inline?

Ou vous pouvez adapter cela à l'exception: Distinct list of objects based on an arbitrary key in LINQ

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 { 

    public class BusinessObject { 
     public Guid Guid { get; set; } 
    } 

    public class BusinessObjectGuidEqualityComparer<T> : IEqualityComparer<T> where T : BusinessObject { 
     #region IEqualityComparer<T> Members 

     public bool Equals(T x, T y) { 
      return (x == null && y == null) || (x != null && y != null && x.Guid.Equals(y.Guid)); 
     } 

     /// </exception> 
     public int GetHashCode(T obj) { 
      if (obj == null) { 
       throw new ArgumentNullException("obj"); 
      } 

      return obj.GetHashCode(); 
     } 

     #endregion 
    } 

    class Program { 
     static void Main(string[] args) { 

      var comparer = new BusinessObjectGuidEqualityComparer<BusinessObject>(); 

      List<BusinessObject> list1 = new List<BusinessObject>() { 
       new BusinessObject() {Guid = Guid.NewGuid()}, 
       new BusinessObject() {Guid = Guid.NewGuid()} 
      }; 

      List<BusinessObject> list2 = new List<BusinessObject>() { 
       new BusinessObject() {Guid = Guid.NewGuid()}, 
       new BusinessObject() {Guid = Guid.NewGuid()}, 
       null, 
       null, 
       list1[0] 
      }; 

      var toRemove = list1.Except(list2, comparer).ToArray(); 
      var toAdd = list2.Except(list1, comparer).ToArray(); 

      // toRemove.Length == 1 
      // toAdd.Length == 2 
      Console.ReadKey(); 
     } 
    } 
} 
+0

C'est ça! J'ai reconnu, que les membres IEqualityComparer sont appelés quand je l'itère à travers, donc j'ai essayé toRemove.ToList(). ForEach (...); et cela fonctionne. – Enyra

1

Les méthodes de votre comparateur d'égalité ne correspondent pas. Vous comparez le GUID des objets, mais la méthode GetHashCode utilise l'implémentation par défaut qui est basée sur la référence, pas le GUID. Comme différentes instances obtiendront différents codes de hachage bien qu'ils aient le même GUID, la méthode Equals ne sera jamais utilisée.

Obtenir le code de hachage pour le GUID dans la méthode GetHashCode, car c'est ce que vous comparez:

public int GetHashCode(T obj) { 
    if (obj == null) { 
     throw new ArgumentNullException("obj"); 
    } 
    return obj.Guid.GetHashCode(); 
} 
+0

Oui, vous avez raison, mais cela ne fonctionne pas pas encore résolu le problème, parce que GetHashCode ni Equals sont appelés. – Enyra

3

Essayez:

public int GetHashCode(T obj) { 
    return obj == null ? 0 : obj.Guid.GetHashCode(); 
} 

Votre hachage code doit correspondre à l'égalité (ou moins, ne pas le contredire); et votre égalité dit "les nulls sont égaux, sinon comparez le guid". En interne, je m'attends à Except utilise un HashSet<T>, ce qui explique pourquoi obtenir GetHashCode droite is so important.


Voici mon banc d'essai (en utilisant ce qui précède GetHashCode) qui fonctionne très bien:

public abstract class BusinessObject { 
    public Guid Guid { get; set; } 
} 
class Area : BusinessObject { 
    public string Name { get; set; } 
    static void Main() { 
     Guid guid = Guid.NewGuid(); 
     List<Area> areas = new List<Area> { 
      new Area { Name = "a", Guid = Guid.NewGuid() }, 
      new Area { Name = "b", Guid = guid }, 
      new Area { Name = "c", Guid = Guid.NewGuid() }, 
     }; 
     List<Area> allocatedAreas = new List<Area> { 
      new Area { Name = "b", Guid = guid}, 
      new Area { Name = "d", Guid = Guid.NewGuid()}, 
     }; 
     BusinessObjectGuidEqualityComparer<Area> comparer = 
      new BusinessObjectGuidEqualityComparer<Area>(); 
     IEnumerable<Area> toRemove = areas.Except(allocatedAreas, comparer); 
     foreach (var row in toRemove) { 
      Console.WriteLine(row.Name); // shows a & c, since b is allocated 
     } 
    } 
} 

Si votre version ne fonctionne pas, vous allez devoir poster quelque chose sur la façon dont vous êtes en l'utilisant, car cela fonctionne bien pour moi (ci-dessus).

+0

Dans la documentation IEqualityComparer est défini, GetHashCode lève cette exception. Donc je suppose que je l'ai mis en place corrent. Mais de toute façon ça ne résoud pas mon problème, les membres IEqualityComparer ne sont pas du tout appelés. – Enyra

+0

Ma conjecture, trébuché par l'exécution différée. –

+0

Pourrait être, pourrait être ;-p –

Questions connexes