Abstract une implémentation IEqualityComparer ou remplacer le comparateur par défaut à utiliser la méthode Distinct

StackOverflow https://stackoverflow.com/questions/4250381

Question

Je suis en train de trouver un List<Author> distinct donné une List<BlogPost> où chaque BlogPost possède une propriété Author. J'ai trouvé la méthode d'extension de Distinct() dans les génériques et je suis en train de l'utiliser. Tout d'abord, laissez-moi vous expliquer ma boucle et où je veux l'utiliser, alors je vais vous expliquer mes classes et où je vais avoir du mal.

Essayer d'utiliser ici distincts

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();
}

D'après ce que j'ai lire sur MSDN , soit Distinct() utilise le comparateur par défaut ou un passé dans comparateur. J'espérais (je obviosuly ne sais pas si cela est faisable) pour écrire un comparateur dans un seul endroit et être en mesure de l'utiliser pour tous mes cours car ils sont tous comparer par l'opération de l'égalité exactement la même (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

Mon égale méthode implémentée dans BasePage compare la propriété GUID qui est unique à chacun. Quand j'appelle Distinct() sur un Author il ne semble pas fonctionner. Est-il possible que je peux conclure le comparateur dans un endroit et toujours être en mesure de l'utiliser plutôt que d'avoir à écrire quelque chose comme class AuhorComparer : IEqualityComparer<Auhor> depuis que je avais besoin alors 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 pas passer quoi que ce soit à Distinct()?

Était-ce utile?

La solution

L'opération de Distinct est probablement pas ici la meilleure solution parce que vous finissez par la construction d'une liste potentiellement très grande avec les doublons pour ensuite rétrécir immédiatement à des éléments distincts. Il est probablement préférable de simplement commencer par un HashSet<Author> pour éviter la construction de 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 voulez utiliser Distinct alors la meilleure voie est de mettre en œuvre IEquatable du type de Author. Lorsqu'ils ne sont pas donné une IEqualityComparer explicite les méthodes de Distinct et d'autres LINQ finiront par défaut en utilisant la mise en œuvre de IEquatable du type. En général, par EqualityComprare<T>.Default

Autres conseils

Equals outrepassée devraient travailler pour vous. Une chose qui pourrait aller mal que GetHashCode n'est pas redéfinie à côté Equals, que les directives cadres dictent devrait se produire.

Le code ne montre que 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; }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top