更新* 我很抱歉......我的示例代码中包含一个错误,导致了许多我不理解的答案。
代替Console.WriteLine("3. this.Equals " + (go1.Equals(go2)));
我打算写
Console.WriteLine("3. this.Equals " + (go1.Equals(sb2)));
我试图找出如何成功确定两个泛型类型值是否相等。根据Mark Byers对this question的回答,我认为我可以使用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));
}
}
现在我定义一个包含GenericObject<StringBuilder>
的{{1}}实例。如果我在这个GenericObject实例上调用new StringBuilder("StackOverflow")
,我希望获得true
,但我得到Equals(new StringBuilder("StackOverflow")
。
示例程序显示:
false
对于比较两个StringBuilder对象的三种方法,只有StringBuilder.Equals实例方法(第三行)返回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)));
}
}
。这是我的预期。但是在比较GenericObject对象时,它的Equals()方法(第三行)返回true
。有趣的是,第四种比较方法 返回false
。我认为第三和第四次比较实际上是在做同样的事情。
我原以为true
。因为在GenericObject类的Equals()方法中,true
和value
都是Value
类型,在这种情况下是T
。基于Mark Byers在this question中的回答,我希望StringBuilder
方法使用StringBuilder的Equals()方法。正如我所示,StringBuilder的Equal()方法确实返回Value.Equals()
。
我甚至尝试过
true
但也会返回false。
所以,这里有两个问题:
public bool Equals(T value) {
return EqualityComparer<T>.Default.Equals(Value, value);
}
?true
方法,以便 返回Equals
?答案 0 :(得分:6)
根据Marc Gravell's answer的建议,问题在于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);
}
答案 1 :(得分:5)
您的代码看起来很好。这里的问题是StringBuilder有一组令人困惑的Equals是矛盾的。特别是,Equals(StringBuilder)不同意Equals(object),即使对象 是一个StringBuilder。
所有EqualityComparer<T>
需要是一个理智的Equals(对象)实现。界面(IEquatable<T>
)是可选的。不幸的是,StringBuilder没有这个(至少,与你的第三个测试使用的Equals(StringBuilder)相比)。
但总的来说,建议是:使用EqualityComparer<T>
;这支持:
答案 2 :(得分:3)
第3行与通用对象没有调用自定义写入方法。相反,它正在调用基础Object.Equals(object)
。要调用自定义方法,您需要传入T
而不是GenericObject<T>
。类似于:go1.Equals(go2.Value)
答案 3 :(得分:2)
正如Eric Lippert在回答question时所说的那样 - 在编译时执行重载解析。
如果您查看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 );
}
回答您的问题如何确定两个泛型类型值是否相等?。你可以做两件事。
GenericObject<T>
中的所有对象,请确保所有至少覆盖Equals
。GenericObject<T>.Equals(T val)
中执行一些反射法术来手动执行后期绑定。答案 4 :(得分:1)
您可以实现IEquatable<T>
,也可以实现实现IEqualityComparer<T>
的比较器类。
确保检查相等性的value
是不可变的,并且仅在类的初始化时设置。
另一个考虑因素是实现IComparer<T>
,当你实现这个时,你不必担心哈希码,因此,也可以为可变类型/字段实现。
一旦您 properly 在课堂上实施IEquatable<T>
,您的问题就会得到解决。
更新:
调用return EqualityComparer<T>.Default.Equals(Value, value);
基本上会返回相同的结果,因为没有实现IEqualityComparer<T>
...
答案 5 :(得分:1)
首先比较typeof()的输出,所以你要确保你比较相同类型的对象,然后在X类上编写一个Equals方法,它接受另一个X类实例,并比较所有属性...一旦你找到不同的东西,返回假,否则继续前进,直到你返回真实。
干杯:)
答案 6 :(得分:0)
详细说明Gideon's answer(请投票支持他,而不是我的):你定义的方法有签名
bool GenericOjbect<T>::Equals( T )
您的代码正在调用
bool GenericOjbect<T>::Equals( GenericOjbect<T> )
从Object
继承(未覆盖)。