De acordo com a documentação do ==
operador no MSDN ,
Para tipos de valor predefinidos, o operador de igualdade (==) retornará true se os valores de seus operandos forem iguais, caso contrário, false. Para tipos de referência diferentes de string, == retorna true se seus dois operandos se referem ao mesmo objeto. Para o tipo de string, == compara os valores das strings. Os tipos de valores definidos pelo usuário podem sobrecarregar o operador == (consulte o operador). Os tipos de referência definidos pelo usuário também podem, embora por padrão == se comporte como descrito acima para os tipos de referência predefinidos e definidos pelo usuário.
Então, por que esse snippet de código falha na compilação?
bool Compare<T>(T x, T y) { return x == y; }
Eu recebo o erro Operador '==' não pode ser aplicado a operandos do tipo 'T' e 'T' . Eu me pergunto por que, desde que eu entenda, o ==
operador é predefinido para todos os tipos?
Edit: Obrigado a todos. Inicialmente, não percebi que a declaração se referia apenas a tipos de referência. Eu também pensei que a comparação bit a bit é fornecida para todos os tipos de valor, que agora sei que não estão corretos.
Mas, no caso de eu estar usando um tipo de referência, o ==
operador usaria a comparação de referência predefinida ou a versão sobrecarregada do operador se um tipo o definisse?
Edit 2: Por tentativa e erro, aprendemos que o ==
operador usará a comparação de referência predefinida ao usar um tipo genérico irrestrito. Na verdade, o compilador usará o melhor método possível para o argumento de tipo restrito, mas não procurará mais. Por exemplo, o código abaixo sempre será impresso true
, mesmo quando Test.test<B>(new B(), new B())
for chamado:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
fonte
==
não é permitido entre dois operandos do mesmo tipo. Isso vale parastruct
tipos (exceto tipos "predefinidos") que não sobrecarregam ooperator ==
. Como um exemplo simples, tente o seguinte:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, então você não pode verificarkvp1 == kvp2
porqueKeyValuePair<,>
é uma estrutura, não é um tipo predefinido de C # e não sobrecarrega ooperator ==
. No entanto, é dado um exemplovar li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
com o qual você não pode fazere1 == e2
(aqui temos a estrutura aninhadaList<>.Enumerator
(chamada"List`1+Enumerator[T]"
pelo tempo de execução) que não sobrecarrega==
).bool
de umvoid
...Respostas:
"... por padrão == se comporta como descrito acima para os tipos de referência predefinidos e definidos pelo usuário."
O tipo T não é necessariamente um tipo de referência; portanto, o compilador não pode fazer essa suposição.
No entanto, isso será compilado porque é mais explícito:
Continue com a pergunta adicional: "Mas, caso eu esteja usando um tipo de referência, o operador == usaria a comparação de referência predefinida ou a versão sobrecarregada do operador se um tipo o definisse?"
Eu pensaria que o == no Generics usaria a versão sobrecarregada, mas o teste a seguir demonstra o contrário. Interessante ... eu adoraria saber o porquê! Se alguém souber, por favor, compartilhe.
Resultado
Inline: Sobrecarregado == chamado
Genérico:
Pressione qualquer tecla para continuar . . .
Acompanhamento 2
Quero ressaltar que alterar meu método de comparação para
faz com que o operador == sobrecarregado seja chamado. Eu acho que sem especificar o tipo (como um local ), o compilador não pode inferir que ele deve usar o operador sobrecarregado ... embora eu ache que teria informações suficientes para tomar essa decisão, mesmo sem especificar o tipo.
fonte
Equals
método substituído (não no==
operador).==
entre tipos genéricosT
eT
, a melhor sobrecarga é encontrada, considerando as restrições que são transportadasT
(existe uma regra especial de que nunca incluirá um tipo de valor para isso (o que daria um resultado sem sentido), portanto, deve haver alguma restrição que garanta que é um tipo de referência). No Follow Up 2 , se você entrar comDerivedTest
objetos eDerivedTest
deriva,Test
mas introduz uma nova sobrecarga==
, terá o "problema" novamente. Qual sobrecarga é chamada, é "queimada" na IL no tempo de compilação.Como outros já disseram, ele só funcionará quando T for restrito a ser um tipo de referência. Sem restrições, você pode comparar com nulo, mas apenas nulo - e essa comparação sempre será falsa para os tipos de valores não nulos.
Em vez de chamar Igual, é melhor usar uma
IComparer<T>
- e se você não tiver mais informações,EqualityComparer<T>.Default
é uma boa opção:Além de qualquer outra coisa, isso evita boxe / fundição.
fonte
Em geral,
EqualityComparer<T>.Default.Equals
deve fazer o trabalho com qualquer coisa que implementeIEquatable<T>
, ou que tenha umaEquals
implementação sensata .Se, no entanto,
==
eEquals
forem implementados de maneira diferente por algum motivo, meu trabalho com operadores genéricos deve ser útil; suporta as versões do operador de (entre outros):fonte
Tantas respostas, e nenhuma explica o PORQUÊ? (que Giovanni perguntou explicitamente) ...
Os genéricos do .NET não agem como modelos C ++. Nos modelos C ++, a resolução de sobrecarga ocorre depois que os parâmetros reais do modelo são conhecidos.
Nos genéricos do .NET (incluindo C #), a resolução de sobrecarga ocorre sem conhecer os parâmetros genéricos reais. As únicas informações que o compilador pode usar para escolher a função a chamar são provenientes de restrições de tipo nos parâmetros genéricos.
fonte
==
funciona para todos os tipos, sejam tipos de referência ou tipos de valor. Essa deveria ser a pergunta à qual acho que você não respondeu.==
não funciona para todos os tipos de valor. Mais importante, ele não tem o mesmo significado para todos os tipos; portanto, o compilador não sabe o que fazer com ele.==
. Você pode incluir essa parte também em sua resposta como eu acho que esse é o ponto principal aquiA compilação não pode saber que T não pode ser uma estrutura (tipo de valor). Então você tem que dizer que só pode ser do tipo de referência, eu acho:
É porque se T poderia ser um tipo de valor, poderia haver casos em
x == y
que seria mal formado - nos casos em que um tipo não tem um operador == definido. O mesmo acontecerá com isso, que é mais óbvio:Isso também falha, porque você poderia passar um tipo T que não teria uma função foo. O C # obriga a garantir que todos os tipos possíveis sempre tenham uma função foo. Isso é feito pela cláusula where.
fonte
Parece que sem a restrição de classe:
Deve-se perceber que, enquanto
class
restritoEquals
no==
operador herdaObject.Equals
, enquanto o de uma estrutura substituiValueType.Equals
.Observe que:
também fornece o mesmo erro do compilador.
Até agora não entendo por que a comparação de operadores de igualdade de tipo de valor é rejeitada pelo compilador. Eu realmente sei que isso funciona:
fonte
Object.Equals
mas testa a igualdade de referência. Por exemplo,Compare("0", 0.ToString())
retornaria false, pois os argumentos seriam referências a seqüências distintas, ambas com zero como único caractere.NullReferenceException
pudesse acontecer.Bem, no meu caso, eu queria testar o operador de igualdade. Eu precisava chamar o código sob os operadores de igualdade sem definir explicitamente o tipo genérico. As recomendações
EqualityComparer
não foram úteis como métodoEqualityComparer
chamadoEquals
, mas não o operador de igualdade.Aqui está como eu consegui isso trabalhando com tipos genéricos criando um
LINQ
. Ele chama o código certo para operadores==
e!=
:fonte
Há uma entrada do MSDN Connect para isso aqui
A resposta de Alex Turner começa com:
fonte
Se você quiser garantir que os operadores do seu tipo personalizado sejam chamados, faça isso por meio de reflexão. Basta obter o tipo usando seu parâmetro genérico e recuperar o MethodInfo para o operador desejado (por exemplo, op_Equality, op_Inequality, op_LessThan ...).
Em seguida, execute o operador usando o método Invoke do MethodInfo e passe os objetos como parâmetros.
Isso chamará seu operador sobrecarregado e não aquele definido pelas restrições aplicadas no parâmetro genérico. Pode não ser prático, mas pode ser útil para testar a unidade de seus operadores ao usar uma classe base genérica que contém alguns testes.
fonte
Eu escrevi a seguinte função olhando o último msdn. Pode facilmente comparar dois objetos
x
ey
:fonte
return ((IComparable)(x)).CompareTo(y) <= 0;
O acima funcionará porque == é resolvido no caso de tipos de referência definidos pelo usuário.
No caso de tipos de valor, == pode ser substituído. Nesse caso, "! =" Também deve ser definido.
Eu acho que esse poderia ser o motivo, ele não permite a comparação genérica usando "==".
fonte
==
token é usado para dois operadores diferentes. Se para os tipos de operando especificados existir uma sobrecarga compatível do operador de igualdade, essa sobrecarga será usada. Caso contrário, se os dois operandos forem tipos de referência compatíveis entre si, uma comparação de referência será usada. Observe que noCompare
método acima, o compilador não pode dizer que o primeiro significado se aplica, mas pode dizer o segundo significado, então o==
token o utilizará mesmo queT
sobrecarregue o operador de verificação de igualdade (por exemplo, se for do tipoString
) .O
.Equals()
trabalho para mimTKey
é um tipo genérico.fonte
x.Id.Equals
não éid.Equals
. Presumivelmente, o compilador sabe algo sobre o tipo dex
.