6

此代码片段来自 C# in Depth

    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

当 main 方法更改为

    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 方法时,它会发生什么变化?

4

3 回答 3

13

对于字符串,您可能不打算使用引用相等。要在泛型方法中访问平等和不平等,最好的选择是:

EqualityComparer<T>.Default.Equals(x,y); // for equality
Comparer<T>.Default.Compare(x,y); // for inequality

IE

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));

你会得到truetrue因为编译器和运行时被设计为对字符串有效;您使用的任何文字都是“实习的”,并且每次在您的 AppDomain 中使用相同的实例。如果可能,编译器(而不是运行时)也会执行 concat - 即

    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”(实习生)、“My name is”(实习生)和“My name is Jon”的两个不同实例。因此==返回 true,引用相等返回 false。但是值相等 ( EqualityComparer<T>.Default) 仍然会返回 true。

于 2009-06-30T04:23:44.163 回答
5

今天学到了一个新东西。

我猜乔恩在其中一个问题中说过,我试图回答。

当您使用连接构建字符串时,== 将为 2 个匹配值的字符串返回 true,但它们不指向相同的引用(我认为,这应该是由于字符串实习。Jon 指出字符串实习适用于常量或文字)。

在通用版本中,它调用 object.ReferenceEquals (这与 == 不同。对于字符串,== 进行值比较)。

结果,连接版本返回 false,而常量(文字字符串)版本返回 true。

编辑:我认为乔恩必须在身边以更好的方式解释这一点:)
懒惰的我,我已经买了这本书,但还没有开始。:(

于 2009-06-30T04:03:43.487 回答
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

于 2009-06-30T05:59:49.800 回答