En el orden en que su código pega ...
==
se anula. Esto significa que en lugar de "abc" == "ab" + "c"
llamando al ==
predeterminado para tipos de referencia (que compara referencias y no valores) llama al string.Equals(a, b)
.
Ahora bien, esto hace lo siguiente:
- Si los dos son de hecho la misma referencia, vuelva realidad.
- Si alguno de ellos es nulo, devuelve falso (ya habríamos devuelto cierto anteriormente si ambos fueran nulos).
- si las dos son de diferente longitud, devuelve falso;
- Realice un ciclo optimizado a través de una cadena, comparándola char-for-char con el resto (en realidad int-for-int como dos bloques de entradas en la memoria, que es una de las optimizaciones involucradas). Si llega al final sin un desajuste, entonces devuelve verdadero; de lo contrario, devuelve falso.
En otras palabras, se comienza con algo como:
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;
}
Excepto que el paso 4 es una versión basada en puntero con un bucle de desenrollado que debería ser idealmente por lo tanto más rápido. No lo mostraré porque quiero hablar sobre la lógica general.
Hay atajos significativos. El primero está en el paso 1. Dado que la igualdad es reflexiva (la identidad implica igualdad, a == a
), podemos devolver true en nanosegundos incluso para una cadena de varios MB de tamaño, si se compara con ella.
El paso 2 no es un atajo, porque es una condición que debe probarse, pero tenga en cuenta que, como ya habremos devuelto cierto para (string)null == (string)null
, no necesitamos otra rama. Entonces, el orden de las llamadas está orientado a un resultado rápido.
El paso 3 permite dos cosas. Ambos atajos en cadenas de diferente longitud (siempre falso) y significa que no se puede disparar accidentalmente al final de una de las cuerdas que se comparan en el paso 4.
Tenga en cuenta que este no es el caso para otras comparaciones de cuerdas , ya que por ejemplo WEISSBIER
y weißbier
son diferentes longitudes pero la misma palabra en mayúsculas diferentes, por lo que la comparación insensible a mayúsculas y minúsculas no puede usar el paso 3. Todas las comparaciones de igualdad pueden hacer los pasos 1 y 2 ya que las reglas siempre se mantienen, por lo que debe usarlas solo. algunos pueden hacer el paso 3.
Por lo tanto, aunque se equivoca al sugerir que se trata de referencias en lugar de valores que se comparan, es cierto que las referencias se comparan primero como un atajo muy significativo. Tenga en cuenta también que las cadenas internas (cadenas colocadas en el grupo interno por compilación o por string.Intern
llamado) activarán por lo tanto este atajo. Este sería el caso en el código de su ejemplo, ya que el compilador habrá utilizado la misma referencia en cada caso.
Si sabe que una cadena fue internada puede confiar en esto (simplemente haga una prueba de igualdad de referencia), pero incluso si no está seguro, puede beneficiarse de ella (la prueba de igualdad de referencia reducirá al mínimo) Algo de tiempo).
Si tiene un montón de cadenas en las que querrá probar algunas de ellas una contra la otra a menudo, pero no desea extender su vida útil en memoria tanto como lo hace la internación, entonces podría usar XmlNameTable o LockFreeAtomizer (que pronto se llamará ThreadSafeAtomizer y el documento se movió a http://hackcraft.github.com/Ariadne/documentation/html/T_Ariadne_ThreadSafeAtomizer_1.htm) debería haber sido nombrado para la función en lugar de los detalles de implementación en primer lugar).
La primera se usa internamente por XmlTextReader
y, por lo tanto, por el resto de System.Xml
y también puede ser utilizada por otros códigos. Este último lo escribí porque quería una idea similar, que fuera segura para llamadas simultáneas, para diferentes tipos, y donde pudiera anular la comparación de igualdad.
En cualquier caso, si pone 50 cadenas diferentes que son todas "abc" en él, obtendrá una sola referencia de referencia "abc" permitiendo que los demás sean basura. Si sabe que esto ha sucedido, puede depender solo del ReferenceEquals
, y si no está seguro, igual se beneficiará del atajo cuando sea el caso.
@EricJ. Pero si tienen la misma dirección de memoria, se deduce que ** debe ** tener el mismo contenido (es la * misma * instancia después de todo). – Yuck
@Yuck: solo si el internamiento es parte de la especificación, y no solo un detalle de implementación. Además, las cadenas en diferentes dominios de aplicaciones podrían ser iguales y tener diferentes direcciones. – psr
@psr Correcto, es por eso que el cheque condicional. Si la referencia es la misma, ya terminaste, eso es todo. De lo contrario, tendría que comparar los contenidos de cada variable para determinar la igualdad lógica. – Yuck