Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IEquatable与lt;T>;,IEqualityComparer<;T>;,以及在自定义对象集合上使用LINQ时重写.Equals()?_C#_Linq - Fatal编程技术网

C# IEquatable与lt;T>;,IEqualityComparer<;T>;,以及在自定义对象集合上使用LINQ时重写.Equals()?

C# IEquatable与lt;T>;,IEqualityComparer<;T>;,以及在自定义对象集合上使用LINQ时重写.Equals()?,c#,linq,C#,Linq,在比较一个自定义对象的两个集合时,使用Linq的.Except()方法有一些困难 我已经从对象派生了我的类,并实现了对Equals()、GetHashCode()、运算符=和的重写=。我还创建了一个CompareTo()方法 在我的两个集合中,作为一个调试实验,我从每个列表中选取第一项(重复项),并将它们进行比较,如下所示: itemListA[0].Equals(itemListB[0]); // true itemListA[0] == itemListB[0];

在比较一个自定义对象的两个集合时,使用Linq的.Except()方法有一些困难

我已经从
对象
派生了我的类,并实现了对
Equals()
GetHashCode()
、运算符
=
的重写=。我还创建了一个
CompareTo()
方法

在我的两个集合中,作为一个调试实验,我从每个列表中选取第一项(重复项),并将它们进行比较,如下所示:

itemListA[0].Equals(itemListB[0]);     // true
itemListA[0] == itemListB[0];          // true
itemListA[0].CompareTo(itemListB[0]);  // 0
在这三种情况下,结果都是我想要的。但是,当我使用Linq的EXPECT()方法时,不会删除重复项:

我注意到,当我从
IEquatable
继承时,我可以使用
IEquatable
IEquatable
;当我使用其中一个时,
Equals()
签名的要求会改变。推荐的方法是什么

我正在努力实现的目标:

我希望能够使用Linq(Distinct/Except)以及标准的相等运算符(
=
!=
),而无需重复代码。如果两个对象的名称相同且
LastUpdate
属性在秒数(用户指定)公差范围内,则比较应允许将其视为相等

编辑:


显示
GetHashCode()
code.

我在对象中用于相等的基本模式如下所示。请注意,只有两个方法具有特定于对象的实际逻辑。其余的只是输入这两个方法的锅炉板代码

class MyObject : IEquatable<MyObject> { 
  public bool Equals(MyObject other) { 
    if (Object.ReferenceEquals(other, null)) {
      return false;
    }

    // Actual equality logic here
  }

  public override int GetHashCode() { 
    // Actual Hashcode logic here
  }

  public override bool Equals(Object obj) {
    return Equals(obj as MyObject);
  }

  public static bool operator==(MyObject left, MyObject right) { 
    if (Object.ReferenceEquals(left, null)) {
      return Object.ReferenceEquals(right, null);
    }
    return left.Equals(right);
  }

  public static bool operator!=(MyObject left, MyObject right) {
    return !(left == right);
  }
}
类MyObject:IEquatable{
公共布尔等于(MyObject其他){
if(Object.ReferenceEquals(other,null)){
返回false;
}
//这里的实际相等逻辑
}
公共重写int GetHashCode(){
//这里的实际哈希代码逻辑
}
公共覆盖布尔等于(对象对象对象){
返回等于(obj作为MyObject);
}
公共静态布尔运算符==(MyObject左,MyObject右){
if(Object.ReferenceEquals(左,空)){
return Object.ReferenceEquals(右,null);
}
返回左。等于(右);
}
公共静态布尔运算符!=(MyObject左,MyObject右){
返回!(左==右);
}
}
如果遵循此模式,则实际上不需要提供自定义的
IEqualityComparer
EqualityComparer.Default
就足够了,因为它依赖于
IEquatable
来执行相等性检查

您不能“在
LastUpdate
上允许一些容差”,然后使用使用严格值
LastUpdate
GetHashCode()
实现

假设
this
实例在
23:13:13.933
处具有
LastUpdate
,而
obj
实例具有
23:13:13.932
。那么这两个可能会与你的宽容理念相媲美。但如果是这样,它们的哈希代码必须是相同的数字。但是,除非您非常幸运,否则这种情况不会发生,因为
DateTime.GetHashCode()
不应该为这两次提供相同的哈希值


此外,您的
Equals
方法在数学上最可能是一个传递关系。“近似等于”不能成为可传递的。它的传递闭包是标识一切的简单关系。

重写
对象、等于
对象、获取hashcode
、实现
IEquatable
、或提供
IEqualityComparer
都无关紧要。它们都可以工作,只是方式略有不同

1) 从
对象
重写
等于
GetHashCode

从某种意义上说,这是基本情况。假设您能够编辑类型以确保这两个方法的实现符合需要,那么它通常会工作。在很多情况下,这样做没有错

2) 实施
IEquatable

这里的关键点是,您可以(也应该)实现
IEquatable
。这与#1之间的关键区别在于,对于
Equals
方法,您有强大的键入功能,而不仅仅是让它使用
对象
。这对程序员来说更方便(增加了类型安全性),也意味着任何值类型都不会被装箱,因此这可以提高自定义结构的性能。如果你这样做的话,你应该经常在#1之外做,而不是代替。让这里的
Equals
方法在功能上与
对象不同。Equals
将是……糟糕的。不要那样做

3) 实施
IEqualityComparer

这与前两个完全不同。这里的想法是,对象没有得到它自己的哈希代码,或者看它是否等于其他东西。这种方法的要点是,一个对象不知道如何正确地获取它的散列,也不知道它是否等于其他内容。可能是因为您不控制该类型的代码(即第三方库),他们也不愿意重写该行为,或者他们确实重写了该行为,但您只是希望在这个特定上下文中拥有自己独特的“平等”定义

在本例中,您创建了一个完全独立的“comparer”对象,它接收两个不同的对象,并通知您它们是否相等,或者一个对象的哈希代码是什么。使用此解决方案时,无论
Equals
GetHashCode
方法在类型本身中做什么,您都不会使用它



请注意,所有这些都与
=
操作符完全无关,后者是它自己的beast。

不是自动从
对象派生的类?对于
CompareTo
方法,还需要
IComparable(T)
public class MyObject : Object
{
    public string Name {get; set;}
    public DateTime LastUpdate {get; set;}

    public int CompareTo(MyObject other)
    {
        // ...
    }

    public override bool Equals(object obj)
    {
        // allows some tolerance on LastUpdate
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + Name.GetHashCode();
            hash = hash * 23 + LastUpdate.GetHashCode();
            return hash;
        }
    }

    // Overrides for operators
}
class MyObject : IEquatable<MyObject> { 
  public bool Equals(MyObject other) { 
    if (Object.ReferenceEquals(other, null)) {
      return false;
    }

    // Actual equality logic here
  }

  public override int GetHashCode() { 
    // Actual Hashcode logic here
  }

  public override bool Equals(Object obj) {
    return Equals(obj as MyObject);
  }

  public static bool operator==(MyObject left, MyObject right) { 
    if (Object.ReferenceEquals(left, null)) {
      return Object.ReferenceEquals(right, null);
    }
    return left.Equals(right);
  }

  public static bool operator!=(MyObject left, MyObject right) {
    return !(left == right);
  }
}