2009-06-30 17 views
6

Este fragmento de código es de C# en Profundidadsobrecarga de operadores en genéricos Métodos

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)); 
    } 

La salida del fragmento de código anterior es

True 
False 

Cuando se cambia el método principal para

static void Main() 
    { 
     string intro1 = "My name is Jon"; 
     string intro2 = "My name is Jon"; 
     Console.WriteLine(intro1 == intro2); 
     Console.WriteLine(AreReferencesEqual(intro1, intro2)); 
    } 

La salida del fragmento de código anterior es

True 
True 

No puedo entender por qué?

EDITAR: Una vez que comprenda la interferencia de cadenas, las siguientes preguntas no se aplican.

¿Cómo se reciben los parámetros en el método genérico AreReferencesEqual en el segundo fragmento de código?

¿Qué cambios en el tipo de cadena cuando se concatena para que el operador == no llame al método Equals sobrecargado del tipo String?

+6

Recuerde, _generics_ no son _templates_. El compilador sobrecarga la resolución en el operador == UNA VEZ y TODAS las construcciones genéricas usan el resultado de ese análisis. No hacemos un análisis para Comparar y otro para Comparar y un tercero para Comparar . Hacemos el análisis UNA VEZ. Al comparar T con T donde se sabe que T es cualquier clase, lo único que podemos hacer es hacer que == signifique "comparar por referencia". Por lo tanto, SIEMPRE significa "comparar por referencia" sin importar lo que T sea. –

Respuesta

13

En el caso de las cadenas, es probable que no vaya a utilizar la igualdad referencia Para acceder a la igualdad y la desigualdad en métodos genéricos, lo mejor es:.

EqualityComparer<T>.Default.Equals(x,y); // for equality 
Comparer<T>.Default.Compare(x,y); // for inequality 

es decir

static bool AreValuesEqual<T>(T first, T second) 
    where T : class 
{ 
    return EqualityComparer<T>.Default.Equals(first,second); 
} 

esto sigue utilizando el Equals sobrecargado, pero maneja nulos, etc también. Para la desigualdad, este se encarga de los nulos, y ambos IComparable<T> y IComparable.

Para otros operadores, consulte MiscUtil.


Re la pregunta; en el caso de:

string intro1 = "My name is Jon"; 
    string intro2 = "My name is Jon"; 
    Console.WriteLine(intro1 == intro2); 
    Console.WriteLine(AreReferencesEqual(intro1, intro2)); 

Se obtiene true, true porque el compilador y el tiempo de ejecución está diseñado para ser eficiente con cuerdas; todos los literales que usa son "internados" y la misma instancia se usa cada vez en su AppDomain. El compilador (en lugar de tiempo de ejecución) también lo hace el concat si es posible - es decir,

string intro1 = "My name is " + "Jon"; 
    string intro2 = "My name is " + "Jon"; 
    Console.WriteLine(intro1 == intro2); 
    Console.WriteLine(AreReferencesEqual(intro1, intro2)); 

es exactamente el mismo código que el ejemplo anterior. No hay diferencia en absoluto. Sin embargo, si lo fuerza a concatenar cadenas en tiempo de ejecución, se supone que es probable que sean efímeras, por lo que son no internados/reutilizados. Entonces en el caso:

string name = "Jon"; 
    string intro1 = "My name is " + name; 
    string intro2 = "My name is " + name; 
    Console.WriteLine(intro1 == intro2); 
    Console.WriteLine(AreReferencesEqual(intro1, intro2)); 

tiene 4 cadenas; "Jon" (internados), "Mi nombre es" (internados) y dos instancias diferentes de "Mi nombre es Jon". Por lo tanto, == devuelve verdadero y la igualdad de referencia devuelve falso. Pero value-equality (EqualityComparer<T>.Default) seguiría siendo verdadero.

+0

+1. Como siempre bien explicado. – shahkalpesh

+0

@Marc: gracias, eso fue sucinto – abhilash

5

Aprendí una cosa nueva hoy.

Supongo que Jon dijo en una de las preguntas, intenté responder.

Cuando construye una cadena usando concatenación, == devolverá verdadero para 2 cadenas de valor coincidente pero no apuntan a la misma referencia (lo cual pensé que debería ser debido a la cadena de intercesión. Jon señaló que la cadena funciona para constantes o literales).

En la versión genérica, está llamando a object.ReferenceEquals (que es diferente de ==. En el caso de una cadena, == hace una comparación de valores).

Como resultado, la versión concatenada devuelve falso mientras que la versión constante (cadena literal) devuelve verdadero.

EDIT: Creo que Jon debe estar presente para explicar esto de una manera mucho mejor :)
Lazy me, he comprado el libro pero todavía no lo he empezado. :(

+1

No necesito que lo explique yo mismo - Marc ha hecho un gran trabajo :) (y su explicación también está bien). –

2

No tiene nada que ver con el método genérico, pero la creación de instancias de las cuerdas

en la primera versión del principal tiene:

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

que crea 4 cuerdas. Dos de los cuales son constantes de tiempo de compilación, llamadas "Jon" y "My name is"; sin embargo, al inicializar intro1 e intro2 el compilador no puede decir que el nombre siempre es jon y resuelve el valor del tiempo de ejecución creando una nueva cadena para cada uno de intro1 e intro2.

en la segunda versión

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

sólo tiene una cadena y eso es una constante de tiempo de compilación: "Mi nombre es Jon" y se asigna esa cadena a ambos intro1 y intro2 y por eso

AreReferencesEqual(intro1, intro2) 

devuelve falso en el primer caso y verdadero en el segundo