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

#c# #types #comparison #equals #equals-operator

#c# #типы #сравнение #равно #equals-оператор

Вопрос:

Я знаю, что в 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 перегружает operator==(), Equals и GetHashCode. Заставляет его действовать как тип значения.

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

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

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

Ответ №1:

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

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

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

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

Ответ №2:

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

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

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

Ответ №3:

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

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

 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 # определяет равенство строк следующим образом:

Хотя string является ссылочным типом, операторы равенства (== и !=) определены для сравнения значений строковых объектов, а не ссылок (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.Equals

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

  • Элемент списка
  • x.Equals(x) возвращает true.
  • x.Equals(y) возвращает то же значение, что и y.Equals(x).
  • если (x.Equals(y) amp;amp; y.Equals(z)) возвращает true , то x.Equals(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(Object) и оператора равенства, объект.Метод ReferenceEquals(Object) не может быть переопределен. Из-за этого, если вы хотите проверить две ссылки на объекты на равенство, и вы не уверены в реализации метода 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)
  

Ответ №4:

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

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

Ответ №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); , как правило, единственными интернированными строками являются те, которые являются константами времени компиляции.