Resumo uma implementação de conformidade ou substituição do comparador padrão para usar o método distinto
-
27-09-2019 - |
Pergunta
Estou tentando encontrar um distinto List<Author>
dado a List<BlogPost>
onde cada um BlogPost
tem um Author
propriedade. Eu encontrei o Distinct()
Método de extensão em genéricos e estou tentando usá -lo. Primeiro, deixe -me explicar meu loop e onde quero usá -lo, depois explicarei minhas aulas e onde estou tendo problemas.
Tentando usar distintos aqui
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();
}
Baseado no que eu Leia no MSDN, Distinct()
ou usa o comparador padrão ou um passado no comparador. Eu esperava (eu obviamente não sei se isso é factível) para escrever uma comparação em um ponto e ser capaz de usá -lo para todas as minhas aulas, pois todas elas se comparam exatamente pela mesma operação de igualdade (que compara o GUID
propriedade de cada classe).
Todas as minhas aulas herdam do BasePage
classe:
public class BasePage : System.Web.UI.Page, IBaseTemplate, IEquatable<IBaseTemplate>, IEqualityComparer<IBaseTemplate>
public class Author : BasePage
public class BlogPost : BasePage
Meu método igual a ser implementado em BasePage
compara o GUID
propriedade exclusiva para cada um. Quando eu ligo Distinct()
em um Author
Não parece funcionar. Existe alguma maneira de encerrar o comparador em um só lugar e sempre ser capaz de usá -lo, em vez de ter que escrever algo como class AuhorComparer : IEqualityComparer<Auhor>
Como eu precisava escrever a mesma coisa para cada aula, toda vez que eu quiser usar Distinct()
. Ou Posso substituir o comparador padrão de alguma forma, então não tenho que passar nada para Distinct()
?
Solução
o Distinct
A operação provavelmente não é a melhor solução aqui, porque você acaba construindo uma lista potencialmente muito grande, com duplicatas apenas para encolher imediatamente para elementos distintos. Provavelmente é melhor apenas começar com um HashSet<Author>
Para evitar construir a grande lista.
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();
}
Se você quiser usar Distinct
então o melhor caminho é implementar IEquatable
no Author
modelo. Quando não recebeu um explícito IEqualityComparer
a Distinct
e outros métodos LINQ acabarão por usar o uso do IEquatable
implementação no tipo. Geralmente através EqualityComprare<T>.Default
Outras dicas
Overiden iguais deve funcionar para você. Uma coisa que pode estar dando errado é que o GethashCode não é substituído ao lado de iguais, que as diretrizes da estrutura ditam.
O código mostra apenas a idéia principal, que, espero, será útil.
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; }
}