Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.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# Enumerable.Distinct-自定义类必须实现哪些方法才能工作?_C#_.net - Fatal编程技术网

C# Enumerable.Distinct-自定义类必须实现哪些方法才能工作?

C# Enumerable.Distinct-自定义类必须实现哪些方法才能工作?,c#,.net,C#,.net,我已经实现了MSDN所说的每一个必要的功能,加上一些额外的比较接口——似乎什么都不起作用。下面是为LinqPad优化的代码。 结果输出是全部4项,而不是我期望的2项。 请不要将变通方法作为答案发布-我想知道Distinct是如何工作的 void Main() { List<NameClass> results = new List<NameClass>(); results.Add(new NameClass("hello")); results.Add(ne

我已经实现了MSDN所说的每一个必要的功能,加上一些额外的比较接口——似乎什么都不起作用。下面是为LinqPad优化的代码。 结果输出是全部4项,而不是我期望的2项。 请不要将变通方法作为答案发布-我想知道Distinct是如何工作的

void Main()
{
  List<NameClass> results = new List<NameClass>();
  results.Add(new NameClass("hello"));
  results.Add(new NameClass("hello"));
  results.Add(new NameClass("55"));
  results.Add(new NameClass("55"));
  results.Distinct().Dump();
}

// Define other methods and classes here

  public class NameClass : Object
    , IEquatable<NameClass>
    , IComparer<NameClass>
    , IComparable<NameClass>
    , IEqualityComparer<NameClass>
    , IEqualityComparer
    , IComparable
  {

    public NameClass(string name)
    {
      Name = name;
    }

    public string Name { get; private set; }

    public int Compare(NameClass x, NameClass y)
    {
      return String.Compare(x.Name, y.Name);
    }

    public int CompareTo(NameClass other)
    {
      return String.Compare(Name, other.Name);
    }

    public bool Equals(NameClass x, NameClass y)
    {
      return (0 == Compare(x, y));
    }

    public bool Equals(NameClass other)
    {
      return (0 == CompareTo(other));
    }

    public int GetHashCode(NameClass obj)
    {
      return obj.Name.GetHashCode();
    }

    public new int GetHashCode()
    {
      return Name.GetHashCode();
    }

    public new bool Equals(object a)
    {
      var x = a as NameClass;
      if (null == x) { return false; }
      return Equals(x);
    }

    public new bool Equals(object a, object b)
    {
      if (null == a && null == b) { return true;  }
      if (null == a && null != b) { return false; }
      if (null != a && null == b) { return false; }
      var x = a as NameClass;
      var y = b as NameClass; 
      if (null == x && null == y) { return true;  }
      if (null == x && null != y) { return false; }
      if (null != x && null == y) { return false; }
      return x.Equals(y);
    }

    public int GetHashCode(object obj)
    {
      if (null == obj) { return 0; }
      var x = obj as NameClass;
      if (null != x) { return x.GetHashCode(); }
      return obj.GetHashCode();
    }

    public int CompareTo(object obj)
    {
      if (obj == null) return 1;

      NameClass x = obj as NameClass;
      if (x == null) 
      {
        throw new ArgumentException("Object is not a NameClass");
      }
      return CompareTo(x);
    }
  }
Distinct的工作原理:

至少没有用于对象初始比较的Object.GetHashCode的实现:Distinct compares的基本版本实际上首先按Object.GetHashCode放入字典,而不是哈希代码按Object.Equals匹配

确切地说,使用EqualityComparer.Default最终检查相等性注意,如果哈希代码不匹配,它将不会到达比较的那个部分,这就是您的示例无法工作的原因

默认相等比较器default用于比较实现IEquatable泛型接口的类型的值

反过来,实际上允许使用类而不必直接返回到对象。等于:

默认属性检查类型T是否实现System.IEquatable接口,如果是,则返回使用该实现的EqualityComparer。否则,它将返回一个EqualityComparer,使用T提供的Object.Equals和Object.GetHashCode的重写

因此,要使basicdistinct工作,您只需要Equals/GetHashCode的正确版本。IEquatable是可选的,但必须匹配类中GetHashCode的行为

如何修复:

您的示例有一个公共的新int-GetHashCode方法,该方法很可能应该是public,它可以覆盖int-GetHashCode,与Equals相同

请注意,公共新国际。。。并不意味着重写,而是创建隐藏旧方法的新版本。它不会影响通过指向父对象的指针调用方法的调用方

我个人认为,在定义方法时,应该很少使用新方法。一些有用的建议见。

如何使用Distinct:

至少没有用于对象初始比较的Object.GetHashCode的实现:Distinct compares的基本版本实际上首先按Object.GetHashCode放入字典,而不是哈希代码按Object.Equals匹配

确切地说,使用EqualityComparer.Default最终检查相等性注意,如果哈希代码不匹配,它将不会到达比较的那个部分,这就是您的示例无法工作的原因

默认相等比较器default用于比较实现IEquatable泛型接口的类型的值

反过来,实际上允许使用类而不必直接返回到对象。等于:

默认属性检查类型T是否实现System.IEquatable接口,如果是,则返回使用该实现的EqualityComparer。否则,它将返回一个EqualityComparer,使用T提供的Object.Equals和Object.GetHashCode的重写

因此,要使basicdistinct工作,您只需要Equals/GetHashCode的正确版本。IEquatable是可选的,但必须匹配类中GetHashCode的行为

如何修复:

您的示例有一个公共的新int-GetHashCode方法,该方法很可能应该是public,它可以覆盖int-GetHashCode,与Equals相同

请注意,公共新国际。。。并不意味着重写,而是创建隐藏旧方法的新版本。它不会影响通过指向父对象的指针调用方法的调用方


我个人认为,在定义方法时,应该很少使用新方法。本节介绍了一些有用的建议。

您不必实现任何接口,只需正确地获取hashcode和Equals方法即可:

public class NameClass
{
    public NameClass(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    public override bool Equals(object obj)
    {
        var other = obj as NameClass;
        return other != null && other.Name == this.Name;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}

您不必实现任何接口,只需正确地获取hashcode和Equals方法:

public class NameClass
{
    public NameClass(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    public override bool Equals(object obj)
    {
        var other = obj as NameClass;
        return other != null && other.Name == this.Name;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}
:

它使用默认的相等比较器来比较值

:

属性检查类型T是否实现System.IEquatable接口,如果是,则返回使用该实现的EqualityComparer。否则,它将返回一个EqualityComparer,使用T提供的Object.Equals和Object.GetHashCode的重写

:

如果实现IEquatable,还应该重写和的基类实现,以便它们的行为与IEquatable.Equals方法的行为一致

:

重写修饰符是扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现所必需的

因此,您的代码应该如下所示:

public class NameClass : IEquatable<NameClass>
{
    public NameClass(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    // implement IEquatable<NameClass>
    public bool Equals(NameClass other)
    {
        return (other != null) && (Name == other.Name);
    }

    // override Object.Equals(Object)
    public override bool Equals(object obj)
    {
        return Equals(obj as NameClass);
    }

    // override Object.GetHashCode()
    public override GetHashCode()
    {
        return Name.GetHashCode();
    }
}
:

它使用默认的相等比较器来比较值

:

属性检查类型T是否实现System.IEquatable接口,如果是,则返回使用该实现的EqualityComparer。否则,它将返回使用Obj覆盖的EqualityComparer ect.Equals和Object.GetHashCode由T提供

:

如果实现IEquatable,还应该重写和的基类实现,以便它们的行为与IEquatable.Equals方法的行为一致

:

重写修饰符是扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现所必需的

因此,您的代码应该如下所示:

public class NameClass : IEquatable<NameClass>
{
    public NameClass(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    // implement IEquatable<NameClass>
    public bool Equals(NameClass other)
    {
        return (other != null) && (Name == other.Name);
    }

    // override Object.Equals(Object)
    public override bool Equals(object obj)
    {
        return Equals(obj as NameClass);
    }

    // override Object.GetHashCode()
    public override GetHashCode()
    {
        return Name.GetHashCode();
    }
}

我刚刚意识到我弄乱了我的示例代码——我的类是从DependencyObject派生的,而不是Object。我无法重写W GetHashCode或Equals函数,因为DependencyObject类是密封的

我刚刚意识到我把示例代码弄糟了——我的类是从DependencyObject而不是Object派生的。我无法重写W GetHashCode或Equals函数,因为DependencyObject类是密封的

因此,首先,Distinct将根据其文档使用EqualityComparer.Default来比较对象,如果没有提供自定义EqualityComparer,则您没有提供

默认情况下,根据其文档,将查看对象是否实现了IEquatable,如果实现了IEquatable,则将使用Equals的实现

无论类型是否实现IEquatable,EqualityComparer.Default都将使用object.GetHashCode方法获取对象的has代码。不幸的是,IEquatable并不强制您也重写对象的GetHashCode实现,在您的情况下,虽然您确实实现了IEquatable,但您的代码并不重写对象的GetHashCode实现

因此,Distinct实际上为您的类型使用了正确的Equals方法,但它使用了错误的GetHashCode方法。当您对对象进行散列时,如果该类型有一个Equals和GetHashCode实现,则会出现不同步的问题。所发生的事情是,在任何基于散列的集合中,它都会将两个相等的对象发送到不同的bucket,因此它们甚至不会到达各自的Equals方法被调用的点。如果你碰巧运气好,有一个散列集合,并且对象恰好被发送到同一个bucket,那么,由于Equals方法是你想要的,所以它实际上会工作,但是发生这种情况的几率……非常低。在此特定情况下,约2/2147483647,或 9.3e-10

虽然您确实在NameClass中提供了一个新的GetHashCode方法,但它隐藏了对象实现,而不是覆盖它。如果您将GetHashCode实现更改为使用override而不是new,那么您的代码将正常工作。

因此,首先,Distinct将根据其文档使用EqualityComparer。如果未提供自定义equality comparer,则默认情况下比较对象。如果您未提供任何自定义equality comparer,则默认情况下比较对象

默认情况下,根据其文档,将查看对象是否实现了IEquatable,如果实现了IEquatable,则将使用Equals的实现

无论类型是否实现IEquatable,EqualityComparer.Default都将使用object.GetHashCode方法获取对象的has代码。不幸的是,IEquatable并不强制您也重写对象的GetHashCode实现,在您的情况下,虽然您确实实现了IEquatable,但您的代码并不重写对象的GetHashCode实现

因此,Distinct实际上为您的类型使用了正确的Equals方法,但它使用了错误的GetHashCode方法。当您对对象进行散列时,如果该类型有一个Equals和GetHashCode实现,则会出现不同步的问题。所发生的事情是,在任何基于散列的集合中,它都会将两个相等的对象发送到不同的bucket,因此它们甚至不会到达各自的Equals方法被调用的点。如果你碰巧运气好,有一个散列集合,并且对象恰好被发送到同一个bucket,那么,由于Equals方法是你想要的,所以它实际上会工作,但是发生这种情况的几率……非常低。在此特定情况下,约2/2147483647,或 9.3e-10


虽然您确实在NameClass中提供了一个新的GetHashCode方法,但它隐藏了对象实现,而不是覆盖它。如果将GetHashCode实现更改为使用override而不是new,则代码将正常工作。

此公共新int GetHashCode行的用途是什么???它将覆盖System.Object.GetHashCode函数,根据MSDN docs.new中的指定,它不会覆盖但会隐藏基类中的成员。这个公共的新int-GetHashCode行是用于什么的???它覆盖System.Object.GetHashCode func,正如MSDN docs.new中指定的那样,它不重写,而是隐藏基类中的成员。我讨厌override和new-因此需要将两个函数标记为override,但其中一个函数应将new public override int GetHashCode{return Name.GetHashCode;}public override bool Equalsobject a标记为new public override{var x=
a作为名称类;如果null==x{return false;}返回Equalsx;}public new bool equals object a,object b{}MSDN文档非常容易引起误解-它表明,只要类实现了IEquatable,就可以使用该接口。技术上不同的使用EqualityComparer.Default@Drew,它确实被使用了,文档中没有任何误导。谢谢你在这里解释操作顺序。首先调用GetHashCode并将其作为备份。我一直在试图弄清楚为什么没有调用Equals,这是20篇文章中第一篇调用这一点的文章。我讨厌override和new-所以两个函数需要标记为override,但一个函数应该标记为new public override int GetHashCode{return Name.GetHashCode;}public override bool equalobject a{var x=a作为NameClass;如果null==x{return false;}return Equalsx;}public new bool equalobject a,object b{}MSDN文档非常误导-它表明,只要类实现IEquatable,然后将使用该接口。技术上不同的使用EqualityComparer.Default@Drew,它确实被使用了,文档中没有任何误导。谢谢你在这里解释操作顺序。首先调用GetHashCode并将其作为备份。我一直在想为什么没有调用Equals,这是20篇文章中第一篇调用这一点的文章。这是最干净的代码,但请注意,从未使用过IEquatable接口-在其中放置断点,函数不会被命中。@Drew:IEquatable.Equals用于正确重写GetHashCode。这是最干净的代码,但请注意,从未使用过IEquatable接口-在其中放置断点,函数将不会被命中。@Drew:IEquatable.Equals用于正确重写GetHashCode。如果DependencyObject被密封,那么如何从中派生?GetHashCode和Equals函数被密封,不是整个类。它非常清楚地说明了为什么不应该在DependencyObject中重写Equals和GetHashCode。@Drew假设不应该在NameClass上本机定义值相等,您可以定义一个实现IEqualityComparer的单独类,并将其传递给Distinct,以便仍能进行值比较。如果DependencyObject是密封的,那么如何从中派生?GetHashCode和Equals函数是密封的,并非整个类都清楚地说明了为什么不应在DependencyObject中重写Equals和GetHashCode。@Drew鉴于不应在NameClass上本机定义值相等,您可以定义一个实现IEqualityComparer的单独类,并将其传递给Distinct,以便仍能进行值比较。