Сравнение ЗНАЧЕНИЙ и ССЫЛОК типов

#c#

Вопрос:

Я знаю, что в C# существует множество способов сравнения ЗНАЧЕНИЙ и ССЫЛОК, но я все еще немного не понимаю, какой тип выполняет что, когда вы пытаетесь сравнить ЗНАЧЕНИЕ или ССЫЛКУ.

Примеры строк:

 string str = "hello";
string str2 = "hello";

if (str == str2)
{
   Console.WriteLine("Something");
} // Is this a comparison of value?

if (str.Equals(str2))
{
   Console.WriteLine("Something");
} // Is this a comparison of value?

string.ReferenceEquals(str, str2); // Comparison of reference (True)

Console.WriteLine((object)str1 == (object)str2); // Comparison of reference (True)
 

Комментарии:

1. Я считаю, что строки-это частный случай: когда компилятор видит два раза одну и ту же строку, он создает в памяти только одну. Так что значение ИЛИ ссылка в данном случае не имеют значения.

2. Класс string перегружает оператор==(), Equals и GetHashCode. Заставляет его действовать как тип значения.

3. строка — это особый вид ссылочного типа. Лучше используйте что-нибудь другое (StringBuilder) для изучения этого.

4. @Бабуин: правильный вывод, неправильные рассуждения. Две строки в разных местах с одинаковым содержимым будут равны.

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

Ответ №1:

Equals и == будут сравниваться по ссылке по умолчанию, если они не переопределены / перегружены в подклассе. ReferenceEquals всегда будет сравнивать по ссылке.

Строки-это сбивающий с толку тип данных, который можно использовать для экспериментов с этим, потому что они перегружены == для реализации равенства значений; кроме того, поскольку они неизменяемы, C# обычно повторно использует один и тот же экземпляр для одной и той же литеральной строки. В вашем коде str и str2 будет один и тот же объект.

Комментарии:

1. == это должно быть перегружено, а не переопределено.

Ответ №2:

@Inerdia прав в том, что он говорит, но я хотел бы указать причину, по которой строка строится.ReferenceEquals(str, str2) возвращает значение true в примере кода. Поскольку вы определяете обе строки во время компиляции, компилятор может оптимизировать код, чтобы они оба указывали на один и тот же экземпляр строки. Поскольку строки неизменяемы, компилятор знает, что он может это сделать, даже если строка является ссылочным типом. Но если вы измените свой код для динамической генерации одной из строк (как показано ниже), компилятор не сможет выполнить эту оптимизацию. Поэтому в вашем примере кода, если вы измените свой код на:

 string str = "hello";
string str2 = new StringBuilder().Append("he").Append("llo").ToString(); 
 

Затем веревка.Строка ReferenceEquals(str, str2) теперь вернет значение false, так как на этот раз компилятор не может знать, следует ли повторно использовать тот же экземпляр (ссылка на строку).

Ответ №3:

  1. строка.Значения ссылок(str, str2);
    Очевидно, что он сравнивает ссылки.
  2. str.Equals(str2)
    Сначала пытается сравнить ссылки. Затем он пытается сравнить по значению.
  3. str == str2
    Делает то же самое, что и Равные.

Хороший способ сравнить строки-использовать строку.Сравнить. Если вы хотите игнорировать регистр, для этого тоже есть параметр.

Ответ №4:

Равенство и сравнение типов ссылок и строк:

Ссылочные типы работают следующим образом:

 System.Object a = new System.Object();
System.Object b = new System.Object();
a == b;      //returns true
a.Equals(b); //returns false

b = a;
a == b;      //returns true
a.Equals(b); //returns true
 

Поскольку строки являются ссылочными типами, они должны делать то же самое, не так ли? Но они этого не делают!

Документация C# определяет равенство строк следующим образом:

Хотя строка является ссылочным типом, операторы равенства (== и !=) определены для сравнения значений строковых объектов, а не ссылок (7.9.7 Операторы равенства строк). Это делает тестирование на равенство строк более интуитивным.

https://msdn.microsoft.com/en-us/library/362314fe(v=vs.71).aspx
https://msdn.microsoft.com/en-us/library/aa664728(v=vs.71).aspx

Это имеет значение для вашего тестового кода.

 if (str == str2)
{
   Console.WriteLine("Something");
} // This is comparision of value even though string is a referenceType

if (str.Equals(str2))
{
   Console.WriteLine("Something");
} // This is comparison by value too, because Equals is overrided in String class.
 

Имейте в виду, что вы, как программист (или ваш хитрый коллега), можете переопределить .Equals(), изменяя его поведение, то, что вы видите выше, должно произойти. Это не обязательно соответствует вашей кодовой базе-реальности, если вы сомневаетесь, проверьте определение, пометив .Равно() и нажимаем F12.

Добавление для x.Равно

Поведение object.Equals() должно соответствовать этим правилам:

  • Элемент списка
  • x.Равно(x) возвращает значение true.
  • x.Equals(y) возвращает то же значение, что и y.Equals(x).
  • если (x.Равно(y) amp;amp; y.Равно(z)) возвращает значение true, то x.Равно(z) возвращает значение true.
  • Последовательные вызовы x.Equals(y) возвращают одно и то же значение до тех пор, пока объекты, на которые ссылаются x и y, не будут изменены.
  • x.Equals(null) возвращает значение false. https://msdn.microsoft.com/ru-ru/library/ms173147(v=vs.80).aspx

Всякий раз, когда вы сомневаетесь, вы можете вызвать x.ReferenceEquals, он определяется следующим образом:

В отличие от метода Object.Equals(Объект) и оператора равенства, Объект.Метод ReferenceEquals(объект) не может быть переопределен. Из-за этого, если вы хотите проверить две ссылки на объекты на равенство и не уверены в реализации метода Equals, вы можете вызвать этот метод.

https://msdn.microsoft.com/de-de/library/system.object.referenceequals(v=vs.110).aspx

Таким образом:

 System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b);  //returns true
 

В вашем примере компилятор объединяет ваши строки в оптимизации таким образом:

 string str = "hello";
string str2 = "hello";
string.ReferenceEquals(str, str2); // Comparison of reference (True)
 

Такое поведение связано только с оптимизацией компилятора в вашем примере, если мы рандомизируем код, он вернет false:

 string str = "hello";
string str2 = "hello";
if(throwCoin)
{ 
   str2 = "bye";
}   
string.ReferenceEquals(str, str2); // Comparison of reference (False)
 

Ответ №5:

Выдержка из источников .net:

 public bool Equals(string value)
{
  if (this == null)
    throw new NullReferenceException();
  else if (value == null)
    return false;
  else if (object.ReferenceEquals((object) this, (object) value))
    return true;
  else
    return string.EqualsHelper(this, value);
}
 

Поэтому, как правило, сначала проводится сравнение ссылок, и если они не совпадают, он сравнивает значения.

Комментарии:

1. Что тоже не объясняет, почему ReferenceEquals() возвращается true .

2. Мне самому интересно, есть ли какой-нибудь случай, когда ReferenceEquals возвращает false, а EqualsHelper возвращает true. Если такого случая нет, в случае, если строки не равны, метод Equals тратит время, пытаясь сравнить символ за символом.

3. Конечно, есть. Каждый раз, когда вы создаете две строки с одним и тем же текстом, и хотя бы одна из них не интернирована. например. var s1 = new string(' ', 1); var s2 = new string(' ', 1); . Как правило, единственными интернированными строками являются те, которые являются константами времени компиляции.