我正在尝试此代码示例,并在1200px
返回OpTest
时System.Console.WriteLine(s == t);
。有人可以解释一下吗?
false
答案 0 :(得分:16)
您的泛型方法基本上将执行引用相等性检查 - s1
和s2
的值引用不同但相等的字符串。你可以像这样更容易地展示:
string x = "test";
string y = new string(x.ToCharArray());
Console.WriteLine(x == y); // Use string overload, checks for equality, result = true
Console.WriteLine(x.Equals(y)); // Use overridden Equals method, result = true
Console.WriteLine(ReferenceEquals(x, y)); // False because they're different objects
Console.WriteLine((object) x == (object) y); // Reference comparison again - result = false
请注意,OpTest
中的约束不会更改使用哪个==
运算符。这是在编译时根据T
的约束确定的。请注意,运算符永远不会被重写,只会重载。这意味着无论执行时的类型如何,都会在编译时选择实现。
如果您约束T
派生自某个重载==
运算符的类型,那么编译器将使用该重载。例如:
using System;
class SillyClass
{
public static string operator ==(SillyClass x, SillyClass y) => "equal";
public static string operator !=(SillyClass x, SillyClass y) => "not equal";
}
class SillySubclass : SillyClass
{
public static string operator ==(SillySubclass x, SillySubclass y) => "sillier";
public static string operator !=(SillySubclass x, SillySubclass y) => "very silly";
}
class Test
{
static void Main()
{
var x = new SillySubclass();
var y = new SillySubclass();
OpTest(x, y);
}
static void OpTest<T>(T x, T y) where T : SillyClass
{
Console.WriteLine(x == y);
Console.WriteLine(x != y);
}
}
此处OpTest
方法 使用重载运算符 - 但只使用SillyClass
而非SillySubclass
。
答案 1 :(得分:4)
已经有很多答案,但我还有一些额外的补充。如果您遇到这种问题,可以使用ildasm.exe
来查看生成的IL。例如:
public class Foo
{
public static void OpTest_1<T>(T s, T t) where T : class
{
var val = s == t;
}
public static void OpTest_2(string s, string t)
{
var val = s == t;
}
// Does not compile.
//public static void OpTest_3<T>(T s, T t) where T : struct
//{
// var val = s == t;
//}
}
给予OpTest_1
:
.method public hidebysig static void OpTest_1<class T>(!!T s, !!T t) cil managed
{
// Code size 17 (0x11)
.maxstack 2
.locals init ([0] bool val)
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: ret
} // end of method Foo::OpTest_1
所以你看到它调用ceq
来检查引用是否相等。
另一个有这个IL:
.method public hidebysig static void OpTest_2(string s, string t) cil managed
{
// Code size 10 (0xa)
.maxstack 2
.locals init ([0] bool val)
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: ret
} // end of method Foo::OpTest_2
那不使用ceq
,而是使用mscorlib
中的字符串相等操作,并按预期给出结果。
就像我说的那样,只是为了增加另一种研究这个问题的方法。有关更高级别的详细信息,建议您阅读@JonSkeet's answer。
答案 2 :(得分:2)
s == t
方法中的 OpTest<T>
检查引用相等性,而不是值相等。在这种情况下,由于false
类的参考来源不同,它会返回StringBuilder
。
要获得true
值,您需要使用Equals
方法:
public static void OpTest<T>(T s, T t) where T : class
{
System.Console.WriteLine(s.Equals(t));
}
答案 3 :(得分:2)
这是因为您使用的是通用方法,并且您将通用参数专门限制为class
的类型。
默认情况下,泛型类型没有定义相等运算符==
。
将<T>
的可能类型限制为类可以使s == t
成为可能。但是,现在它将使用class
限制指定的默认实现,并使用引用相等。
由于你的一个字符串来自StringBuilder
,它会创建一个新的引用,尽管字符串的内容是相同的。
如果在两种情况下使用相同的字符串文字,它将返回true
,因为文字仅生成一次,然后每次使用时都会引用它。