此代码段来自深度中的C#
static bool AreReferencesEqual<T>(T first, T second)
where T : class
{
return first == second;
}
static void Main()
{
string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
}
上面代码段的输出是
True
False
当主要方法改为
时 static void Main()
{
string intro1 = "My name is Jon";
string intro2 = "My name is Jon";
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
}
上面代码段的输出是
True
True
我无法理解为什么?
编辑:一旦你理解了字符串实习,下面的问题就不适用了。
<击>
如何在第二个代码段中的通用方法AreReferencesEqual
中接收参数? 击>
串联时字符串类型有什么变化,以使==运算符不调用String类型的重载Equals方法?
击>
答案 0 :(得分:13)
在字符串的情况下,您可能不打算使用引用相等性。 要获得通用方法中的平等和不平等,最好打赌:
EqualityComparer<T>.Default.Equals(x,y); // for equality
Comparer<T>.Default.Compare(x,y); // for inequality
即
static bool AreValuesEqual<T>(T first, T second)
where T : class
{
return EqualityComparer<T>.Default.Equals(first,second);
}
这仍然使用重载的Equals
,但也处理空值等。对于不等式,它处理空值,以及IComparable<T>
和IComparable
。
对于其他运营商,请参阅MiscUtil。
回答问题;在以下情况下:
string intro1 = "My name is Jon";
string intro2 = "My name is Jon";
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
你得到true
,true
因为编译器和运行时被设计为对字符串有效;您使用的任何文字都是“interned”,并且每次在AppDomain中都使用相同的实例。如果可能的话,编译器(而不是运行时)也会进行连接 - 即。
string intro1 = "My name is " + "Jon";
string intro2 = "My name is " + "Jon";
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
与前一个示例完全相同的代码。完全没有区别。但是,如果强制它在运行时连接字符串,它会假定它们可能是短暂的,因此它们不实习/重用。所以在这种情况下:
string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
你有4个字符串; “Jon”(实习),“我的名字”(实习)和两个不同的实例“我的名字是Jon”。因此==
返回true,引用相等性返回false。但是价值平等(EqualityComparer<T>.Default
)仍然会返回真实。
答案 1 :(得分:5)
今天学到了一件新东西。
我想Jon在其中一个问题中说过,我试着回答。
使用连接构建字符串时,==将为2个匹配值字符串返回true,但它们不指向相同的引用(我认为,它应该由于字符串实习。Jon指出字符串实习工作对于常数或文字)。
在通用版本中,它调用object.ReferenceEquals(与==不同。在字符串的情况下,==进行值比较)。
因此,连接版本返回false,而常量(文字字符串)版本返回true。
编辑:我认为Jon必须以更好的方式解释这个问题:) 懒惰我,我已经买了这本书,但尚未开始使用它。 :(答案 2 :(得分:2)
它与泛型方法无关,而是字符串的实例化
在main的第一个版本中你有:
string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;
创建4个字符串。其中两个是编译时常量,即“Jon”和“My name is”但是在初始化intro1和intro2时,编译器不能说该名称总是jon并且解析值运行时为每个intro1和intro2创建一个新字符串。
在第二个版本中
string intro1 = "My name is Jon";
string intro2 = "My name is Jon";
你只有一个字符串,那是一个编译时间常量:“我的名字是Jon”,你将该字符串分配给intro1和intro2,这就是为什么
AreReferencesEqual(intro1, intro2)
在第一种情况下返回false,在第二种情况下返回true