Перегрузка операторов в общих методах - PullRequest
6 голосов
/ 30 июня 2009

Этот фрагмент кода из 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 во втором фрагменте кода?

Что меняет тип строки, когда он объединяется, чтобы оператор == не вызывал перегруженный метод Equals типа String?

Ответы [ 3 ]

13 голосов
/ 30 июня 2009

В случае строк вы, вероятно, не собираетесь использовать равенство ссылок. Чтобы получить доступ к равенству и неравенству в общих методах, лучше всего сделать это:

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, потому что компилятор и среда выполнения спроектированы так, чтобы быть эффективными со строками; все литералы, которые вы используете, являются «интернированными», и каждый раз в вашем домене приложений используется один и тот же экземпляр. компилятор (а не среда выполнения) также выполняет 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 строки; «Джон» (интернированный), «Меня зовут» (интернированный) и два разных экземпляра из «Меня зовут Джон». Следовательно, == возвращает true, а равенство ссылок возвращает false. Но равенство значений (EqualityComparer<T>.Default) все равно вернет true.

5 голосов
/ 30 июня 2009

Сегодня узнал что-то новое.

Полагаю, Джон ответил на один из вопросов, на которые я пытался ответить.

Когда вы строите строку с использованием конкатенации, == вернет true для 2 строк соответствующего значения, но они не указывают на одну и ту же ссылку (что, как я думал, должно быть из-за интернирования строк. Джон указал, что интернирование строк работает для константы или литералов).

В общей версии он вызывает object.ReferenceEquals (который отличается от ==. В случае строки == выполняет сравнение значений).

В результате конкатенированная версия возвращает false, тогда как константная (литеральная строка) версия возвращает true.

РЕДАКТИРОВАТЬ: Я думаю, Джон должен быть рядом, чтобы объяснить это гораздо лучше:)
Ленивый я, я купил книгу, но еще не начал ее. (

2 голосов
/ 30 июня 2009

Это не имеет ничего общего с универсальным методом, кроме создания строк

в первой версии main у вас есть:

string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;

, который создает 4 строки. Две из которых являются константами времени компиляции, а именно «Jon» и «My name is», однако при инициализации intro1 и intro2 компилятор не может сказать, что name всегда является jon, и разрешает значение времени выполнения, создавая новую строку для каждого из intro1 и intro2. *

во второй версии

string intro1 = "My name is Jon";
string intro2 = "My name is Jon";

у вас есть только одна строка, и это постоянная времени компиляции: «Меня зовут Джон», и вы присваиваете эту строку как intro1, так и intro2, и поэтому

AreReferencesEqual(intro1, intro2)

возвращает false в первом случае и true во втором

...