В том порядке, в котором ваш код попадает в него ...
==
отменено. Это означает, что вместо "abc" == "ab" + "c"
вызов по умолчанию ==
для ссылочных типов (который сравнивает ссылки, а не значения) он вызывает в string.Equals(a, b)
.
Теперь, это делает следующее:
- Если два действительно являются одной и той же ссылкой, вернуть true.
- Если любой из них равен нулю, вернуть false (мы бы уже вернули true выше, если бы они оба были нулевыми).
- если два значения имеют разную длину, вернуть false;
- Выполните оптимизированный цикл по одной строке, сравнивая ее char-for-char с остальными (на самом деле int-for-int, рассматриваемый как два блока целых чисел в памяти, что является одной из задействованных оптимизаций). Если он достигает конца без несоответствия, верните true, в противном случае верните false.
Другими словами, он начинается с чего-то вроде:
public static bool ==(string x, string y)
{
//step 1:
if(ReferenceEquals(x, y))
return true;
//step 2:
if(ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
//step 3;
int len = x.Length;
if(len != y.Length)
return false;
//step 4:
for(int i = 0; i != len; ++i)
if(x[i] != y[i])
return false;
return true;
}
За исключением того, что шаг 4 является версией на основе указателя с развернутым циклом, который в идеале должен быть быстрее. Я не буду этого показывать, потому что хочу поговорить об общей логике.
Существуют значительные сокращения. Первый шаг - шаг 1. Поскольку равенство является рефлексивным (идентичность влечет за собой равенство a == a
), то мы можем вернуть true в наносекундах даже для строки размером в несколько МБ, если сравнивать с собой.
Шаг 2 не является кратчайшим, потому что это условие, которое необходимо проверить, но учтите, что, поскольку мы уже вернули true для (string)null == (string)null
, нам не нужна другая ветвь. Так что порядок звонков ориентирован на быстрый результат.
Шаг 3 допускает две вещи. Это сокращает строки различной длины (всегда ложные) и означает, что нельзя случайно выстрелить за конец одной из строк, сравниваемых на шаге 4.
Обратите внимание, что это не относится к другим сравнениям строк, так как, например, WEISSBIER
и weißbier
- это разные длины, но одно и то же слово в разных заглавных буквах, поэтому сравнение без учета регистра не может использовать шаг 3. Все сравнения на равенство могут выполнять шаги 1 и 2, так как используемые правила всегда выполняются, поэтому их следует использовать в самостоятельно, только некоторые могут выполнить шаг 3.
Следовательно, несмотря на то, что вы ошибаетесь, полагая, что сравниваются не ссылки, а значения, верно, что ссылки сравниваются в первую очередь как очень существенное сокращение. Также обратите внимание, что интернированные строки (строки, помещенные в внутренний пул путем компиляции или с помощью string.Intern
вызываемого), следовательно, будут часто вызывать этот ярлык. Это будет иметь место в коде в вашем примере, так как компилятор будет использовать одну и ту же ссылку в каждом случае.
Если вы знаете, что строка была интернирована, вы можете зависеть от этого (просто сделайте тест на равенство ссылок), но даже если вы не знаете наверняка, вы можете извлечь из этого пользу (тест на равенство ссылок позволит сократить по крайней мере некоторые времени).
Если у вас есть набор строк, в которых вы захотите часто тестировать некоторые из них друг против друга, но вы не хотите продлевать их время жизни в памяти так же, как интернирование, тогда вы можете использовать XmlNameTable или LockFreeAtomizer (вскоре будет переименован в ThreadSafeAtomizer и документ будет перемещен в http://hackcraft.github.com/Ariadne/documentation/html/T_Ariadne_ThreadSafeAtomizer_1.htm - должен был быть назван для функции, а не для деталей реализации в первую очередь).
Первый используется внутри XmlTextReader
и, следовательно, большинством остальных System.Xml
и может использоваться другим кодом. Последнее я написал, потому что хотел подобную идею, которая была бы безопасна для одновременных вызовов, для разных типов и где я мог переопределить сравнение на равенство.
В любом случае, если вы поместите в него 50 различных строк, которые все являются «abc», вы получите одну ссылку «abc», позволяющую другим собирать мусор. Если вы знаете, что это произошло, вы можете зависеть только от ReferenceEquals
, а если вы не уверены, вы все равно выиграете от быстрого доступа, когда это так.