繁体   English   中英

如何确定两个泛型类型值是否相等?

[英]How to determine if two generic type values are equal?

更新*我很抱歉......我的示例代码包含一个错误,导致了许多我不理解的答案。 代替

Console.WriteLine("3. this.Equals   " + (go1.Equals(go2)));

我打算写

Console.WriteLine("3. this.Equals   " + (go1.Equals(sb2)));

我试图找出如何成功确定两个泛型类型值是否相等。 根据Mark Byers对这个问题的回答,我认为我可以使用value.Equals() ,其中value是泛型类型。 我的实际问题是在LinkedList实现中,但问题可以通过这个更简单的示例来显示。

class GenericOjbect<T> {
    public T Value { get; private set; }
    public GenericOjbect(T value) {
        Value = value;
    }
    public bool Equals(T value) {
        return (Value.Equals(value));
    }
}

现在我定义一个包含new StringBuilder("StackOverflow")GenericObject<StringBuilder>实例。 如果我在这个GenericObject实例上调用Equals(new StringBuilder("StackOverflow") ,我希望得到true ,但我得到了false

一个示例程序显示:

using System;
using System.Text;

class Program
{
    static void Main()
    {
        var sb1 = new StringBuilder("StackOverflow");
        var sb2 = new StringBuilder("StackOverflow");

        Console.WriteLine("StringBuilder compare");
        Console.WriteLine("1. ==            " + (sb1 == sb2));
        Console.WriteLine("2. Object.Equals " + (Object.Equals(sb1, sb2)));
        Console.WriteLine("3. this.Equals   " + (sb1.Equals(sb2)));

        var go1 = new GenericOjbect<StringBuilder>(sb1);
        var go2 = new GenericOjbect<StringBuilder>(sb2);

        Console.WriteLine("\nGenericObject compare");
        Console.WriteLine("1. ==            " + (go1 == go2));
        Console.WriteLine("2. Object.Equals " + (Object.Equals(go1, sb2)));
        Console.WriteLine("3. this.Equals   " + (go1.Equals(sb2)));
        Console.WriteLine("4. Value.Equals  " + (go1.Value.Equals(sb2.Value)));
    }
}

对于比较两个StringBuilder对象的三种方法,只有StringBuilder.Equals实例方法(第三行)返回true 这是我的预期。 但是在比较GenericObject对象时,它的Equals()方法(第三行)返回false 有趣的是,第四种比较方法确实返回了true 我认为第三和第四次比较实际上是在做同样的事情。

我原以为是true 因为在GenericObject类的Equals()方法中, valueValue都是T类型,在本例中是StringBuilder 基于Mark Byers在这个问题中的答案,我希望Value.Equals()方法使用StringBuilder的Equals()方法。 正如我所示,StringBuilder的Equal()方法确实返回true

我甚至试过了

public bool Equals(T value) {
    return EqualityComparer<T>.Default.Equals(Value, value);
}

但这也会返回false。

那么,这里有两个问题:

  1. 为什么代码不能返回true
  2. 我怎么能实现Equals方法,所以它确实返回true

作为建议马克Gravell的答案 ,问题是用StringBuilder Equals(object)的实现,是一个不同Equals(StringBuilder)

然后,您可以忽略该问题,因为您的代码将与任何其他一致实现的类一起使用,或者您可以使用动态来修复问题(再次由Mark Gravell建议)。

但是,鉴于您没有使用C#4(因此没有动态),您可以尝试这种方式:

public bool Equals(T value)
{
   // uses Reflection to check if a Type-specific `Equals` exists...
   var specificEquals = typeof(T).GetMethod("Equals", new Type[] { typeof(T) });
   if (specificEquals != null &&
       specificEquals.ReturnType == typeof(bool))
   {
       return (bool)specificEquals.Invoke(this.Value, new object[]{value});
   }
   return this.Value.Equals(value);
}

你的代码看起来很好。 这里的问题是StringBuilder有一组令人困惑的Equals是矛盾的。 特别是,即使对象 StringBuilder,Equals(StringBuilder)也不同意Equals(object)。

EqualityComparer<T> 需要的只是一个理智的Equals(对象)实现。 接口( IEquatable<T> )是可选的。 不幸的是,StringBuilder没有这个(至少,与你的第三个测试正在使用的Equals(StringBuilder)相比)。

但总的来说,建议是:使用EqualityComparer<T> ; 这支持:

  • 可以通过标准的“解除”规则获得可空的
  • IEquatable-的-T
  • 的Object.Equals

第3行与通用对象不调用自定义写入方法。 相反,它调用基础Object.Equals(object) 要调用自定义方法,需要传入T而不是GenericObject<T> 类似的东西: go1.Equals(go2.Value)

正如Eric Lippert在回答这个问题时所说的那样 - 在编译时执行重载解析。

如果你看一下StringBuilder的实现,你会注意到它重载Equals并且不会覆盖它。 这基本上是问题的根源,为什么StringBuilder.Equals不能像您在示例中所期望的那样工作。

以下面的两个班级为例。 Overloader类似于示例中的StringBuilder ,因为它重载了Equals Overrider非常相似,只是它覆盖了Equals

public class Overloader
{
  public string Str {get;private set;}
  public Overloader (string str) {Str = str;}

  public bool Equals( Overloader str )
  {
    return this.Str.Equals( str );
  }
}

public class Overrider
{
  public string Str {get;private set;}
  public Overrider (string str) {Str = str;}

  public override bool Equals( object obj )
  {
    if ( obj is Overrider )
    {
      return this.Str.Equals( (obj as Overrider).Str );
    }
    return base.Equals( obj );
  }
}

我在我的示例中略微修改了GenericObject<T>类:

class GenericOjbect<T>
{
  public T Value {get;private set;}
  public GenericOjbect( T val ) {Value = val;}

  public bool Equals( T val )
  {
    return Value.Equals( val );
  }

  public override bool Equals( object obj )
  {
    if ( obj is T )
    {
      return this.Equals( ( T )obj );
    }
    if (obj != null && obj is GenericOjbect<T> )
    {
      return this.Equals( ( obj as GenericOjbect<T> ).Value );
    }
    return base.Equals( obj );
  }
}

在此示例程序中,您将看到Overloader (或Stringbuilder )将返回false。 但是, Overrider返回true。

class Program
{
  static void Main( string[] args )
  {
    var goOverloader1 = new GenericOjbect<Overloader>( new Overloader( "StackOverflow" ) );
    var goOverloader2 = new GenericOjbect<Overloader>( new Overloader( "StackOverflow" ) );

    var goOverrider1 = new GenericOjbect<Overrider>( new Overrider( "StackOverflow" ) );
    var goOverrider2 = new GenericOjbect<Overrider>( new Overrider( "StackOverflow" ) );

    Console.WriteLine( "Overrider  : {0}", goOverloader1.Equals( goOverloader2 ) ); //False
    Console.WriteLine( "Overloader : {0}", goOverrider1.Equals( goOverrider2 ) ); //True
  }
}

再次引用Eric Lippert - 在编译时执行重载分辨率。 这意味着编译器基本上会像这样查看GenericObject<T>.Equals( T val )

public bool Equals( T val )
{
  return Value.Equals( (Object) val );
}

回答你的问题如何确定两个泛型类型值是否相等? 你可以做两件事。

  1. 如果您拥有将包装在GenericObject<T> 所有对象,请确保它们至少重写Equals
  2. 您可以在GenericObject<T>.Equals(T val)执行一些反射魔法来手动执行后期绑定。

您可以实现IEquatable<T> ,也可以实现实现IEqualityComparer<T>的比较器类。

确保检查相等性的value是不可变的,并且仅在类的初始化时设置。

另一个考虑因素是实现IComparer<T> ,当你实现这个时,你不必担心哈希码,因此,也可以为可变类型/字段实现。

一旦你在课堂上正确实现IEquatable<T> ,你的问题就会得到解决。

更新:调用return EqualityComparer<T>.Default.Equals(Value, value); 基本上会返回相同的结果,因为没有实现IEqualityComparer<T> ...

首先比较typeof()的输出,所以你要确保你比较相同类型的对象,然后在X类上编写一个Equals方法,它接受另一个X类实例,并比较所有属性......一旦找到不同的东西,返回false,否则继续,直到你返回true。

干杯:)

详细说明基甸的答案 (请提出他的,而不是我的):你定义的方法有签名

bool GenericOjbect<T>::Equals( T )

你的代码正在调用

bool GenericOjbect<T>::Equals( GenericOjbect<T> )

Object继承(未覆盖)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM