zoukankan      html  css  js  c++  java
  • 【转】How to use LINQ methods to compare objects of custom types

    原文:http://blogs.msdn.com/csharpfaq/archive/2009/03/25/how-to-use-linq-methods-to-compare-objects-of-custom-types.aspx

    译文:http://www.cnblogs.com/tianfan/

    LINQ provides a convenient syntax and many useful methods for operating with collections of objects. However, to be correctly processed by LINQ comparison methods such as Distinct or Intersect, a type must satisfy certain requirements.

    Let’s take a look at the Distinct method, which returns all distinct objects from a collection.

    List<int> numbers = new List<int> { 1, 1, 2, 3 };

    var distinctNumbers = numbers.Distinct();

    foreach (var number in distinctNumbers)

    Console.WriteLine(number);

    The output is:

    1

    2

    3

    But what if you want to use the Distinct method for a collection of objects of your own type? For example, like this:

    class Number

    {

    public int Digital { get; set; }

    public String Textual { get; set; }

    }

    class Program

    {

    static void Main(string[] args)

    {

    List<Number> numbers = new List<Number> {

    new Number { Digital = 1, Textual = "one" },

    new Number { Digital = 1, Textual = "one" } ,

    new Number { Digital = 2, Textual = "two" } ,

    new Number { Digital = 3, Textual = "three" } ,

    };

    var distinctNumbers = numbers.Distinct();

    foreach (var number in distinctNumbers)

    Console.WriteLine(number.Digital);

    }

    }

    The code compiles, but the output is different:

    1

    1

    2

    3

    Why did that happen? The answer is in the LINQ implementation details. To be correctly processed by the Distinct method, a type must implement the IEquatable<T> interface and provide its own Equals and GetHashCode methods.

    So, the Number class from the previous example should actually look like this:

    class Number: IEquatable<Number>

    {

    public int Digital { get; set; }

    public String Textual { get; set; }

    public bool Equals(Number other)

    {

    // Check whether the compared object is null.

    if (Object.ReferenceEquals(other, null)) return false;

    // Check whether the compared object references the same data.

    if (Object.ReferenceEquals(this, other)) return true;

    // Check whether the objects’ properties are equal.

    return Digital.Equals(other.Digital) &&

    Textual.Equals(other.Textual);

    }

    // If Equals returns true for a pair of objects,

    // GetHashCode must return the same value for these objects.

    public override int GetHashCode()

    {

    // Get the hash code for the Textual field if it is not null.

    int hashTextual = Textual == null ? 0 : Textual.GetHashCode();

    // Get the hash code for the Digital field.

    int hashDigital = Digital.GetHashCode();

    // Calculate the hash code for the object.

    return hashDigital ^ hashTextual;

    }

    }

    But what if you cannot modify the type? What if it was provided by a library and you have no way of implementing the IEquatable<T> interface in this type? The answer is to create your own equality comparer and pass it as a parameter to the Distinct method.

    The equality comparer must implement the IEqualityComparer<T> interface and, again, provide GetHashCode and Equals methods.

    Here is how the equality comparer for the original Number class might look:

    class NumberComparer : IEqualityComparer<Number>

    {

    public bool Equals(Number x, Number y)

    {

    if (Object.ReferenceEquals(x, y)) return true;

    if (Object.ReferenceEquals(x, null) ||

    Object.ReferenceEquals(y, null))

    return false;

    return x.Digital == y.Digital && x.Textual == y.Textual;

    }

    public int GetHashCode(Number number)

    {

    if (Object.ReferenceEquals(number, null)) return 0;

    int hashTextual = number.Textual == null

    ? 0 : number.Textual.GetHashCode();

    int hashDigital = number.Digital.GetHashCode();

    return hashTextual ^ hashDigital;

    }

    }

    And don’t forget to pass the comparer to the Distinct method:

    var distinctNumbers = numbers.Distinct(new NumberComparer());

    Of course, these rules don't just apply to the Distinct method. For example, the same is true for the Contains, Except, Intersect, and Union methods. In general, if you see that a LINQ method has an overload that accepts the IEqualityComparer<T> parameter, it probably means that to use it for your own data types you need to either implement IEquatable<T> in your class or create your own equality comparer.

  • 相关阅读:
    解决:hdfs: org.apache.hadoop.security.AccessControlException(Permission denied)
    新的开始
    死锁问题------------------------INSERT ... ON DUPLICATE KEY UPDATE*(转)
    hql- 使用like的小坑①
    数美面试检讨
    问题:计算foldRight(1)(_-_) 与foldLeft(1)(_-_)值不一样
    学习笔记之流文件操作01(拷贝文件)
    学习笔记之正则表达式
    数据结构之单链表
    (转)消息中间件(二)MQ使用场景
  • 原文地址:https://www.cnblogs.com/RuiLei/p/1712230.html
Copyright © 2011-2022 走看看