2010-11-22 55 views
0

J'essaie de trouver un List<Author> distinct, étant donné un List<BlogPost>BlogPost a une propriété Author. J'ai trouvé la méthode d'extension Distinct() dans les génériques et j'essaie de l'utiliser. Tout d'abord, laissez-moi vous expliquer ma boucle et où je veux l'utiliser, puis je vais expliquer mes cours et où j'ai des problèmes.Résumé d'une implémentation IEqualityComparer ou remplacement du comparateur par défaut pour utiliser la méthode Distinct

Essayer d'utiliser ici distincte

public List<Author> GetAuthors() { 

    List<BlogPost> posts = GetBlogPosts(); 
    var authors = new List<Author>(); 

    foreach (var bp in posts) { 
    authors.Add(bp.Author); 
    } 

    return authors.Distinct().ToList(); 
} 

Sur la base de ce que j'ai read on MSDN, Distinct() utilise soit le comparateur par défaut ou un passé dans comparateur. J'espérais (je ne sais pas si c'est faisable) d'écrire un comparateur en un point et de pouvoir l'utiliser pour toutes mes classes puisqu'ils se comparent tous par la même opération d'égalité (qui compare la propriété GUID de chaque classe).

Toutes mes classes héritent de la classe BasePage:

public class BasePage : System.Web.UI.Page, IBaseTemplate, IEquatable<IBaseTemplate>, IEqualityComparer<IBaseTemplate> 

public class Author : BasePage 

public class BlogPost : BasePage 

Ma méthode equals mis en œuvre BasePage compare la propriété GUID qui est unique à chacun. Quand j'appelle le Distinct() sur un Author cela ne semble pas fonctionner. Est-il possible que je puisse envelopper le comparateur à un endroit et pouvoir toujours l'utiliser plutôt que d'avoir à écrire quelque chose comme class AuhorComparer : IEqualityComparer<Auhor> puisque j'aurais alors besoin d'écrire la même chose pour chaque classe, chaque fois que je veux utiliser Distinct() . Ou puis-je remplacer le comparateur par défaut en quelque sorte, donc je ne dois rien passer à Distinct()?

Répondre

2

L'opération Distinct n'est probablement pas la meilleure solution ici car vous finissez par construire une liste potentiellement très grande avec des doublons pour ensuite la réduire immédiatement à des éléments distincts. Il est probablement préférable de simplement commencer par HashSet<Author> pour éviter de constituer la grande liste.

public List<Author> GetAuthors() { 
    HashSet<Author> authorSet = new HashSet<Author>(); 
    foreach (var author in GetBlogPosts().Select(x => x.Author)) { 
    authorSet.Add(author); 
    } 
    return authorSet.ToList(); 
} 

Si vous ne souhaitez utiliser Distinct alors la meilleure voie est de mettre en œuvre IEquatable du type Author. Lorsqu'on ne lui donne pas un IEqualityComparer explicite, le Distinct et d'autres méthodes LINQ finiront par utiliser l'implémentation IEquatable sur le type. Habituellement à travers EqualityComprare<T>.Default

+0

Merci pour la suggestion de HashSet. J'ai implémenté votre changement mais j'ai toujours une liste de la même taille. J'ai une liste de 3 posts, deux ont le même auteur, l'autre a un auteur complètement différent. Je reçois une liste de 3 auteurs cependant. –

+0

Le fait que le HashSet contienne le même auteur plus d'une fois prouve-t-il que mes méthodes Equals et GetHashCode sont incorrectement écrites? –

+0

J'ai confirmé que cela ne fonctionnait pas parce que ma méthode 'GetHashCode' n'était pas écrite correctement. Maintenant, j'ai cette approche avec le 'HashSet' fonctionnant. Merci! –

0

Overriden L'égalité devrait fonctionner pour vous. Une chose qui pourrait mal se passer est que GetHashCode ne soit pas remplacé par Equals, ce que les directives du framework imposent.

0

Le code montre seulement l'idée principale, qui, je l'espère, sera utile.

public class Repository 
{ 
    public List<Author> GetAuthors() 
    { 
     var authors = new List<Author> 
         { 
          new Author{Name = "Author 1"}, 
          new Author{Name = "Author 2"}, 
          new Author{Name = "Author 1"} 
         }; 
     return authors.Distinct(new CustomComparer<Author>()).ToList(); 
    } 

    public List<BlogPost> GetBlogPosts() 
    { 
     var blogPosts = new List<BlogPost> 
     { 
      new BlogPost {Text = "Text 1"}, 
      new BlogPost {Text = "Text 2"}, 
      new BlogPost {Text = "Text 1"} 
     }; 
     return blogPosts.Distinct(new CustomComparer<BlogPost>()).ToList(); 
    } 
} 

//This comparer is required only one. 
public class CustomComparer<T> : IEqualityComparer<T> where T : class 
{ 
    public bool Equals(T x, T y) 
    { 
     if (y == null && x == null) 
     { 
      return true; 
     } 
     if (y == null || x == null) 
     { 
      return false; 
     } 
     if (x is Author && y is Author) 
     { 
      return ((Author)(object)x).Name == ((Author)(object)y).Name; 
     } 
     if (x is BlogPost && y is BlogPost) 
     { 
      return ((BlogPost)(object)x).Text == ((BlogPost)(object)y).Text; 
     } 
     //for next class add comparing logic here 
     return false; 
    } 

    public int GetHashCode(T obj) 
    { 
     return 0; // actual generating hash code should be here 
    } 
} 

public class Author 
{ 
    public string Name { get; set; } 
} 

public class BlogPost 
{ 
    public string Text { get; set; } 
}