sobrecarga de operadores em métodos genéricos
-
21-08-2019 - |
Pergunta
Este trecho de código é de C # in Depth
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));
}
A saída do código acima é
True
False
Quando o método principal é alterado 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));
}
A saída do código acima é
True
True
Não consigo entender por quê?
EDIT: Depois de entender-internar cordas seguintes perguntas não se aplicam.
Como são os parâmetros recebidos na AreReferencesEqual
método genérico no segundo trecho de código?
O que muda com o tipo corda quando é concatenado para fazer o operador == não chamar o método Equals sobrecarregada do tipo String?
Solução
No caso de strings, você provavelmente não pretende usar a igualdade de referência. À igualdade de acesso e da desigualdade no métodos genéricos, a sua melhor aposta é a seguinte:
EqualityComparer<T>.Default.Equals(x,y); // for equality
Comparer<T>.Default.Compare(x,y); // for inequality
i.
static bool AreValuesEqual<T>(T first, T second)
where T : class
{
return EqualityComparer<T>.Default.Equals(first,second);
}
Isso ainda usa o Equals
sobrecarregado, mas alças nulos etc também. Para a desigualdade, este alças nulos, e ambos IComparable<T>
e IComparable
.
Para outros operadores, consulte MiscUtil .
Re a questão; no caso de:
string intro1 = "My name is Jon";
string intro2 = "My name is Jon";
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
Você começa true
, true
porque o compilador e runtime foi projetado para ser eficiente com cordas; nenhum literais que você usa são "internada" e a mesma instância é usada cada vez em seu AppDomain. O compilador (em vez de tempo de execução) também faz o concat se possível -. I
string intro1 = "My name is " + "Jon";
string intro2 = "My name is " + "Jon";
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferencesEqual(intro1, intro2));
exatamente o mesmo código como o exemplo anterior. Não há nenhuma diferença em tudo. No entanto, se você forçá-lo a cordas concatenar em tempo de execução, ele assume que eles são susceptíveis de ser de curta duração, por isso são não internados / re-utilizado. Assim, no 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));
Você tem 4 cordas; "Jon" (internados), "Meu nome é" (internados), e duas instâncias diferentes de "Meu nome é Jon". retornos Daí ==
verdade e referência igualdade retorna false. Mas o valor-igualdade (EqualityComparer<T>.Default
) ainda retornar verdadeiro.
Outras dicas
aprendi uma coisa nova hoje.
Eu acho que Jon disse em uma das perguntas, eu tentei responder.
Quando você constrói uma string usando concatenação, == retornará true para 2 cordas de valor correspondente, mas eles não apontam para a mesma referência (que eu pensei, deve devido a seqüência de internar. Jon apontou essa seqüência internar obras para constantes ou literais).
Na versão genérica, ele está chamando Object.ReferenceEquals (que é diferente do que ==. Em caso de corda, == faz comparação de valor).
Como resultado, o concatenado versão retorna false enquanto as constantes (string literal) versão retorna verdadeiro.
EDIT: Eu acho que Jon deve ser em torno de explicar isso de uma maneira muito melhor :)
Preguiçoso mim, eu comprei o livro, mas ainda têm de começar a fazer isso. : (
Não tem nada a ver com o método genérico, mas a instanciação das cordas
na primeira versão do principal você tem:
string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;
que cria 4 cordas. Dois dos quais são constantes de tempo de compilação ou seja, "Jon" e "Meu nome é" no entanto ao inicializar intro1 e intro2 o compilador não pode dizer que o nome é sempre jon e resolve o valor de tempo de execução fazer uma nova seqüência para cada um dos intro1 e intro2.
na segunda versão
string intro1 = "My name is Jon";
string intro2 = "My name is Jon";
você só tem uma corda e isso é uma constante de tempo de compilação: "Meu nome é Jon" e você atribui essa seqüência tanto intro1 e intro2 e é por isso
AreReferencesEqual(intro1, intro2)
retornos falsos no primeiro caso e verdadeiras na segunda