如果T是字符串,则泛型类中 == 和等于之间有什么区别。
我准备了2个测试的示例代码。 TestMethod1在第二个断言时失败,但TestMethod2传递。
源代码:
[TestFixture]
public class Test
{
class Foo<T> where T : class
{
public static bool Foo1(T item1, T item2)
{
return item1 == item2;
}
public static bool Foo2(T item1, T item2)
{
return item1.Equals(item2);
}
}
[TestCase]
public void TestMethod1()
{
var name = "str" + 1;
// passes
Assert.IsTrue(Foo<string>.Foo2(name, "str1"));
// fails !
Assert.IsTrue(Foo<string>.Foo1(name, "str1"));
}
[TestCase]
public void TestMethod2()
{
// passes
Assert.IsTrue(Foo<string>.Foo2("str1", "str1"));
// also passes!
Assert.IsTrue(Foo<string>.Foo1("str1", "str1"));
}
}
答案 0 :(得分:2)
在这种情况下,使用==
会解析为System.Reference.Equals
。
.Equals
是一个虚方法,因此将使用被覆盖的版本,在这种情况下,它将是字符串的内容比较。
第一个测试将局部变量与文字(实习)字符串进行比较。
第二个测试比较相同的实习字符串,因此==
和Equals
都返回true。
如果您将TestMethod1()
更改为:
Assert.IsTrue(Foo<string>.Foo1(name, name));
它将通过,因为您正在比较同一个对象,因此引用相等性返回true。
编辑:如果我们添加此方法:
public static bool Foo3(string item1, string item2)
{
return item1 == item2;
}
然后以下测试将返回true
:
Assert.IsTrue(Foo<string>.Foo3(name, "str1"));
因为在这种情况下C#会将==
解析为字符串值检查。
这个问题实际上是this的副本,具有额外的通用复杂性。
编辑2:
好的,是时候深入了解MSIL。 Foo1
看起来像这样:
.method public hidebysig static bool Foo1(!T item1,
!T item2) cil managed
{
// Code size 20 (0x14)
.maxstack 2
.locals init ([0] bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: box !T
IL_0007: ldarg.1
IL_0008: box !T
IL_000d: ceq
IL_000f: stloc.0
IL_0010: br.s IL_0012
IL_0012: ldloc.0
IL_0013: ret
} // end of method Foo`1::Foo1
如果value1等于value2,你可以看到它正在使用ceq
推送1(类型为int32),否则推送0。
以下是Foo2
:
.method public hidebysig static bool Foo2(!T item1,
!T item2) cil managed
{
// Code size 23 (0x17)
.maxstack 2
.locals init ([0] bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: box !T
IL_0007: ldarg.1
IL_0008: box !T
IL_000d: callvirt instance bool [mscorlib]System.Object::Equals(object)
IL_0012: stloc.0
IL_0013: br.s IL_0015
IL_0015: ldloc.0
IL_0016: ret
} // end of method Foo`1::Foo2
这使用System.Object::Equals
- 这是虚方法,并且在参数的具体类型中被覆盖 - 在本例中为string
。
Foo3
(我添加了,它在声明的==
参数上使用string
)如下所示:
.method public hidebysig static bool Foo3(string item1,
string item2) cil managed
{
// Code size 13 (0xd)
.maxstack 2
.locals init ([0] bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: call bool [mscorlib]System.String::op_Equality(string,
string)
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method Foo`1::Foo3
在这里,我们观察到System.String::op_Equality(string, string)
的显式使用,这是字符串值检查比较。
答案 1 :(得分:2)
正如马特已经提到的,你的一半问题是实习
另一半是编译器不知道你的类型参数是string
,并且只是将它视为一些引用类型,因此它调用object
的方法。
但是不要信仰它,让我们来看看IL。
ceq
这里告诉我们没有==
运算符重载,因此对box
ed参数执行引用检查。
Foo`1.Foo1:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: box Test+Foo<>.T
IL_0007: ldarg.1
IL_0008: box Test+Foo<>.T
IL_000D: ceq
IL_000F: stloc.0
IL_0010: br.s IL_0012
IL_0012: ldloc.0
IL_0013: ret
另一方面,在这里我们可以看到callvirt
的{{1}},并且在运行时它将被解析为对实际类型参数的任何实现的调用,即System.Object.Equals
。
string.Equals