Абстрактная реализация IEqualityComparer или переопределение компаратора по умолчанию для использования метода Distinct - PullRequest
0 голосов
/ 23 ноября 2010

Я пытаюсь найти отличное List<Author> с учетом List<BlogPost>, где каждый BlogPost имеет свойство Author.Я нашел метод расширения Distinct() в обобщениях и пытаюсь его использовать.Во-первых, позвольте мне объяснить мой цикл и где я хочу его использовать, затем я объясню мои классы и где у меня возникли проблемы.

Попытка использовать различные здесь

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

Исходя из того, что я прочитал в MSDN , Distinct() использует компаратор по умолчанию или переданный в компаратор.Я надеялся (я, очевидно, не знаю, выполнимо ли это), написать компаратор в одном месте и иметь возможность использовать его для всех моих классов, поскольку все они сравниваются с помощью одной и той же операции равенства (которая сравнивает GUID).свойство каждого класса).

Все мои классы наследуются от класса BasePage:

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

public class Author : BasePage

public class BlogPost : BasePage

Мой метод equals, реализованный в BasePage, сравнивает уникальное свойство GUIDдля каждого.Когда я звоню Distinct() на Author, это не похоже на работу.Есть ли способ, которым я могу обернуть компаратор в одном месте и всегда иметь возможность использовать его вместо того, чтобы писать что-то вроде class AuhorComparer : IEqualityComparer<Auhor>, так как тогда мне нужно было бы писать одно и то же для каждого класса, каждый раз, когда я хочуиспользуйте Distinct(). Или можно ли как-то переопределить компаратор по умолчанию, чтобы мне ничего не приходилось передавать Distinct()?

Ответы [ 3 ]

2 голосов
/ 23 ноября 2010

Операция Distinct, вероятно, не является лучшим решением здесь, потому что вы в конечном итоге создаете потенциально очень большой список с дубликатами только для того, чтобы сразу же сократить его до отдельных элементов. Вероятно, лучше начать с HashSet<Author>, чтобы не создавать большой список.

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

Если вы хотите использовать Distinct, то лучший способ - реализовать IEquatable для типа Author. Если не указано явное IEqualityComparer, Distinct и другие методы LINQ в конечном итоге по умолчанию будут использовать реализацию IEquatable для типа. Обычно через EqualityComprare<T>.Default

0 голосов
/ 23 ноября 2010

Код показывает только основную идею, которая, я надеюсь, будет полезна.

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; }
}
0 голосов
/ 23 ноября 2010

Overriden Equals должен работать на вас.Одна вещь, которая может пойти не так, это то, что GetHashCode не переопределяется вместе с Equals, что должно произойти в соответствии с руководящими принципами.

...