Eu tenho um método genérico definido assim:
public void MyMethod<T>(T myArgument)
A primeira coisa que quero fazer é verificar se o valor de myArgument é o valor padrão para esse tipo, algo como isto:
if (myArgument == default(T))
Mas isso não é compilado porque não garanti que T implementará o operador ==. Então, mudei o código para isso:
if (myArgument.Equals(default(T)))
Agora isso compila, mas falhará se myArgument for nulo, o que é parte do que estou testando. Posso adicionar uma verificação nula explícita como esta:
if (myArgument == null || myArgument.Equals(default(T)))
Agora isso parece redundante para mim. O ReSharper está sugerindo que eu mude a parte nula myArgument == para myArgument == default (T), que é onde eu comecei. Existe uma maneira melhor de resolver este problema?
Eu preciso para apoiar ambos os tipos de referências e tipos de valor.
if (myArgument?.Equals( default(T) ) != null )
.true
em qualquer caso, porqueEquals
sempre será chamado para tipos de valor, poismyArgument
não pode sernull
nesse caso e o resultado deEquals
(um booleano) nunca seránull
.Respostas:
Para evitar o boxe, a melhor maneira de comparar os genéricos pela igualdade é com
EqualityComparer<T>.Default
. Isso respeitaIEquatable<T>
(sem boxe) e tambémobject.Equals
lida com todas asNullable<T>
nuances "levantadas". Conseqüentemente:Isso corresponderá a:
Nullable<T>
fonte
Person
,p1.Equals(p2)
dependeria de sua implementaçãoIEquatable<Person>
na API pública ou via implementação explícita - ou seja, o compilador pode ver umEquals(Person other)
método público . Contudo; em genéricos , a mesma IL é usada para todosT
; umT1
que implementaIEquatable<T1>
precisa ser tratado de forma idêntica a umT2
que não - portanto não, ele não localizará umEquals(T1 other)
método, mesmo que exista em tempo de execução. Nos dois casos, também há onull
que pensar (qualquer um dos objetos). Então, com os genéricos, eu usaria o código que eu publiquei.Que tal agora:
O uso do
static object.Equals()
método evita a necessidade de você fazer anull
verificação você mesmo. Qualificar explicitamente a chamadaobject.
provavelmente não é necessário, dependendo do seu contexto, mas normalmente prefixo asstatic
chamadas com o nome do tipo apenas para tornar o código mais solúvel.fonte
Consegui localizar um artigo do Microsoft Connect que discute esse problema com alguns detalhes:
Aqui está o que você pode fazer ...
Eu validei que ambos os métodos funcionam para uma comparação genérica dos tipos de referência e valor:
ou
Para fazer comparações com o operador "==", você precisará usar um destes métodos:
Se todos os casos de T derivarem de uma classe base conhecida, você poderá informar o compilador usando restrições de tipo genérico.
O compilador reconhece como executar operações no
MyBase
e não lançará o "Operador '==' não pode ser aplicado aos operandos do tipo 'T' e 'T'" que você está vendo agora.Outra opção seria restringir T a qualquer tipo que implemente
IComparable
.E, em seguida, use o
CompareTo
método definido pela interface IComparable .fonte
Tente o seguinte:
isso deve compilar e fazer o que você quiser.
fonte
Equals
métodoIEqualityComparer
leva dois argumentos, os dois objetos a serem comparados; portanto, não é redundante.(Editado)
Marc Gravell tem a melhor resposta, mas eu queria postar um trecho de código simples que trabalhei para demonstrá-lo. Basta executar isso em um aplicativo de console simples em C #:
Mais uma coisa: alguém com o VS2008 pode tentar isso como um método de extensão? Estou preso em 2005 aqui e estou curioso para ver se isso seria permitido.
Edit: Aqui está como fazê-lo funcionar como um método de extensão:
fonte
Para lidar com todos os tipos de T, incluindo onde T é um tipo primitivo, você precisará compilar nos dois métodos de comparação:
fonte
Vai haver um problema aqui -
Se você permitir que isso funcione para qualquer tipo, o padrão (T) sempre será nulo para os tipos de referência e 0 (ou estrutura cheia de 0) para os tipos de valor.
Provavelmente, esse não é o comportamento que você procura. Se você deseja que isso funcione de maneira genérica, provavelmente precisará usar a reflexão para verificar o tipo de T e manipular tipos de valores diferentes dos tipos de referência.
Como alternativa, você pode colocar uma restrição de interface nisso, e a interface pode fornecer uma maneira de verificar o padrão da classe / estrutura.
fonte
Eu acho que você provavelmente precisará dividir essa lógica em duas partes e verificar se há nulo primeiro.
No método IsNull, contamos com o fato de que os objetos ValueType não podem ser nulos por definição; portanto, se o valor for uma classe que deriva de ValueType, já sabemos que não é nulo. Por outro lado, se não for um tipo de valor, podemos apenas comparar o valor convertido com um objeto contra nulo. Poderíamos evitar a verificação do ValueType indo diretamente para uma conversão para o objeto, mas isso significaria que um tipo de valor seria encaixotado, o que é algo que provavelmente queremos evitar, pois implica que um novo objeto é criado no heap.
No método IsNullOrEmpty, estamos verificando o caso especial de uma sequência. Para todos os outros tipos, estamos comparando o valor (que já sabe que não é nulo) com o valor padrão, que para todos os tipos de referência é nulo e, para os tipos de valor, geralmente é uma forma de zero (se eles são integrais).
Usando esses métodos, o seguinte código se comporta conforme o esperado:
fonte
Método de extensão com base na resposta aceita.
Uso:
Alterne com null para simplificar:
Uso:
fonte
Eu uso:
fonte
Não sei se isso funciona com seus requisitos ou não, mas você pode restringir T a um tipo que implemente uma interface como IComparable e, em seguida, use o método ComparesTo () dessa interface (que o IIRC suporta / lida com nulos) como este :
Provavelmente, existem outras interfaces que você poderia usar também IEquitable, etc.
fonte
@ilitirit:
O operador '==' não pode ser aplicado a operandos do tipo 'T' e 'T'
Não consigo pensar em uma maneira de fazer isso sem o teste nulo explícito seguido pela invocação do método ou objeto Equals.Equals, conforme sugerido acima.
Você pode criar uma solução usando o System.Comparison, mas na verdade isso vai acabar com muito mais linhas de código e aumentar substancialmente a complexidade.
fonte
Eu acho que você estava perto.
Agora isso compila, mas falhará se
myArgument
for nulo, o que é parte do que estou testando. Posso adicionar uma verificação nula explícita como esta:Você só precisa reverter o objeto no qual os iguais estão sendo chamados para uma abordagem elegante com segurança nula.
fonte