簡體   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