Como verifico se um determinado objeto é anulável, por outras palavras, como implementar o seguinte método ...
bool IsNullableValueType(object o)
{
...
}
EDIT: Estou procurando tipos de valor anuláveis. Eu não tinha tipos de árbitros em mente.
//Note: This is just a sample. The code has been simplified
//to fit in a post.
public class BoolContainer
{
bool? myBool = true;
}
var bc = new BoolContainer();
const BindingFlags bindingFlags = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance
;
object obj;
object o = (object)bc;
foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
obj = (object)fieldInfo.GetValue(o);
}
obj agora se refere a um objeto do tipo bool
( System.Boolean
) com valor igual a true
. O que eu realmente queria era um objeto do tipoNullable<bool>
Então, agora, como solução alternativa, decidi verificar se o é anulável e criar um invólucro anulável em torno de obj.
Respostas:
Existem dois tipos de anuláveis -
Nullable<T>
e de referência.Jon me corrigiu dizendo que é difícil obter o tipo se estiver na caixa, mas você pode usar os genéricos: - então e quanto a seguir. Na verdade, esse é o tipo de teste
T
, mas o usoobj
puramente do parâmetro para inferência de tipo genérica (para facilitar a chamada) - funcionaria quase de forma idêntica sem oobj
parâmetro.Mas isso não funcionará tão bem se você já inseriu o valor em uma variável de objeto.
Documentação da Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type
fonte
T
é um parâmetro genérico restrito porT : struct
, entãoT
não é permitidoNullable<>
, portanto você não precisa verificar nesse caso! Eu sei que o tipoNullable<>
é uma estrutura, mas em C # a restriçãowhere T : struct
exclui especificamente tipos de valor anuláveis. A especificação diz: "Observe que, embora classificado como um tipo de valor, um tipo anulável (§4.1.10) não atende à restrição de tipo de valor".Existe uma solução muito simples usando sobrecargas de método
http://deanchalk.com/is-it-nullable/
excerto:
então
fonte
True
que seria retornado.System.Nullable<>
). Se você dizobject g = e;
e depoisValueTypeHelper.IsNullable(g)
, o que espera obter?A pergunta "Como verificar se um tipo é anulável?" é na verdade "Como verificar se um tipo é
Nullable<>
?", que pode ser generalizado para "Como verificar se um tipo é um tipo construído de algum tipo genérico?", para que ele não apenas responda à pergunta "ÉNullable<int>
umNullable<>
?", mas também "éList<int>
umList<>
?".A maioria das soluções fornecidas usa o
Nullable.GetUnderlyingType()
método, que obviamente funcionará apenas com o caso deNullable<>
. Como não vi a solução reflexiva geral que funcionaria com qualquer tipo genérico, decidi adicioná-la aqui para posteridade, mesmo que essa pergunta já tenha sido respondida há muito tempo.Para verificar se um tipo é alguma forma de
Nullable<>
usar reflexão, primeiro você precisa converter seu tipo genérico construído, por exemploNullable<int>
, na definição de tipo genéricoNullable<>
,. Você pode fazer isso usando oGetGenericTypeDefinition()
método daType
classe Você pode comparar o tipo resultante comNullable<>
:O mesmo pode ser aplicado a qualquer tipo genérico:
Vários tipos podem parecer iguais, mas um número diferente de argumentos de tipo significa que é um tipo completamente diferente.
Como os
Type
objetos são instanciados uma vez por tipo, é possível verificar a igualdade de referência entre eles. Portanto, se você quiser verificar se dois objetos têm a mesma definição de tipo genérico, você pode escrever:Se você deseja verificar se um objeto é anulável, em vez de a
Type
, use a técnica acima, juntamente com a solução de Marc Gravell, para criar um método bastante simples:fonte
Nullable.GetUnderlyingType()
que já é fornecida pela estrutura. Por que não usar apenas o método na estrutura? Bem, você deveria. É mais claro, conciso e melhor testado. Mas, no meu post, estou tentando ensinar como usar a reflexão para obter as informações desejadas, para que alguém possa aplicá-las a qualquer tipo (substituindotypeof(Nullable<>)
por qualquer outro tipo). Se você olhar para as fontes deGetUnderlyingType()
(original ou descompilado), verá que é muito semelhante ao meu código.Isso funciona para mim e parece simples:
Para tipos de valor:
fonte
Bem, você poderia usar:
... mas um objeto em si não é anulável ou não - um tipo é. Como você estava planejando usar isso?
fonte
A maneira mais simples de descobrir é:
fonte
Nullable
tipo, mas com semântica diferente. Na minha situação, devo apoiarnull
como um valor válido e também não apoiar nenhum valor. Então, um criou umOptional
tipo. Como era necessário oferecer suporte anull
valores, eu também tive que implementar código para manipularNullable
valores como parte da minha implementação. É daí que esse código veio.Existem dois problemas aqui: 1) teste para verificar se um Tipo é anulável; e 2) teste para verificar se um objeto representa um Tipo anulável.
Para a edição 1 (testando um tipo), aqui está uma solução que eu usei em meus próprios sistemas: solução de verificação TypeIsNullable
Para o problema 2 (testando um objeto), a solução de Dean Chalk acima funciona para tipos de valor, mas não para tipos de referência, pois o uso da sobrecarga <T> sempre retorna falso. Como os tipos de referência são inerentemente anuláveis, o teste de um tipo de referência sempre deve retornar verdadeiro. Por favor, veja a nota [Sobre "nulidade"] abaixo para obter uma explicação dessas semânticas. Assim, aqui está a minha modificação na abordagem de Dean:
E aqui está minha modificação no código de teste do cliente para a solução acima:
A razão pela qual modifiquei a abordagem de Dean em IsObjectNullable <T> (T t) é que sua abordagem original sempre retornava falsa para um tipo de referência. Como um método como IsObjectNullable deve poder manipular valores do tipo de referência e como todos os tipos de referência são inerentemente anuláveis, se um tipo de referência ou um nulo for passado, o método sempre retornará true.
Os dois métodos acima podem ser substituídos pelo seguinte método único e obter a mesma saída:
No entanto, o problema com essa última abordagem de método único é que o desempenho sofre quando um parâmetro Nullable <T> é usado. Leva muito mais tempo do processador para executar a última linha desse método único do que para permitir que o compilador escolha a segunda sobrecarga de método mostrada anteriormente quando um parâmetro do tipo Nullable <T> é usado na chamada IsObjectNullable. Portanto, a solução ideal é usar a abordagem de dois métodos ilustrada aqui.
CAVEAT: esse método funciona de maneira confiável apenas se for chamado usando a referência do objeto original ou uma cópia exata, conforme mostrado nos exemplos. No entanto, se um objeto anulável estiver encaixotado em outro Tipo (como objeto, etc.) em vez de permanecer em seu formulário Nullable <> original, esse método não funcionará de maneira confiável. Se o código que chama esse método não estiver usando a referência original do objeto sem caixa ou uma cópia exata, ele não poderá determinar com segurança a nulidade do objeto usando esse método.
Na maioria dos cenários de codificação, para determinar a nulidade, deve-se confiar no teste do Tipo do objeto original, não na sua referência (por exemplo, o código deve ter acesso ao Tipo original do objeto para determinar a nulidade). Nesses casos mais comuns, IsTypeNullable (consulte o link) é um método confiável para determinar a anulabilidade.
PS - Sobre "nulidade"
Devo repetir uma declaração sobre a nulidade que fiz em um post separado, que se aplica diretamente ao tratamento adequado deste tópico. Ou seja, acredito que o foco da discussão aqui não deve ser como verificar se um objeto é do tipo Nullable genérico, mas se é possível atribuir um valor nulo a um objeto do seu tipo. Em outras palavras, acho que deveríamos determinar se um tipo de objeto é anulável, não se é anulável. A diferença está na semântica, a saber, as razões práticas para determinar a nulidade, que geralmente é tudo o que importa.
Em um sistema que usa objetos com tipos possivelmente desconhecidos até o tempo de execução (serviços da Web, chamadas remotas, bancos de dados, feeds etc.), um requisito comum é determinar se um nulo pode ser atribuído ao objeto ou se o objeto pode conter um nulo. A execução de tais operações em tipos não anuláveis provavelmente produzirá erros, geralmente exceções, que são muito caros, tanto em termos de desempenho quanto de requisitos de codificação. Para adotar a abordagem altamente preferida de evitar proativamente esses problemas, é necessário determinar se um objeto de um Tipo arbitrário é capaz de conter um valor nulo; ou seja, se é geralmente 'anulável'.
Em um sentido muito prático e típico, a nulidade em termos do .NET não implica necessariamente que o Type de um objeto seja uma forma de Nullable. De fato, em muitos casos, os objetos têm tipos de referência, podem conter um valor nulo e, portanto, são todos anuláveis; nenhum deles tem um tipo nulo. Portanto, para propósitos práticos na maioria dos cenários, o teste deve ser realizado para o conceito geral de nulidade, versus o conceito de Nullable, dependente da implementação. Portanto, não devemos ficar concentrados apenas no tipo .NET Nullable, mas incorporar nosso entendimento de seus requisitos e comportamento no processo de foco no conceito geral e prático de nulidade.
fonte
A solução mais simples que encontrei é implementar a solução da Microsoft ( Como: Identificar um Tipo Nulo (Guia de Programação em C #) ) como um método de extensão:
Isso pode ser chamado assim:
Isso também parece uma maneira lógica de acessar,
IsNullable()
porque se encaixa em todos os outrosIsXxxx()
métodos daType
classe.fonte
Tenha cuidado, ao encaixar um tipo anulável (
Nullable<int>
ou int? Por exemplo):Torna-se um tipo de referência verdadeiro, para que você perca o fato de ser anulável.
fonte
Talvez um pouco fora de tópico, mas ainda algumas informações interessantes. Eu encontro muitas pessoas que usam
Nullable.GetUnderlyingType() != null
para identificar se um tipo é anulável. Obviamente, isso funciona, mas a Microsoft recomenda o seguintetype.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
(consulte http://msdn.microsoft.com/en-us/library/ms366789.aspx ).Eu olhei para isso do ponto de vista do desempenho. A conclusão do teste (um milhão de tentativas) abaixo é que, quando um tipo é anulável, a opção Microsoft oferece o melhor desempenho.
Nullable.GetUnderlyingType (): 1335ms (3 vezes mais lento)
GetGenericTypeDefinition () == typeof (Anulável <>): 500ms
Sei que estamos falando de pouco tempo, mas todo mundo adora ajustar os milissegundos :-)! Então, se seu chefe quer reduzir alguns milissegundos, este é o seu salvador ...
fonte
Console.WriteLine
é de facto fora da área doseada;)Assert
deve estar fora do circuito. Em segundo lugar, você também deve testá-lo contra não-nulos e também para testar casos falsos. Fiz um teste semelhante (2 nulables e 4 não nulos) e recebo ~ 2 segundosGetUnderlyingType
e ~ 1 segundo porGetGenericTypeDefinition
, ou seja,GetGenericTypeDefinition
é duas vezes mais rápido (não três vezes).GetUnderlyingType
foi 2,5 vezes mais lento. Com apenas não nulos - desta vez, ambos são pescoço e pescoço.GetUnderlyingType
é útil quando você precisa verificar a anulabilidade e obter o tipo subjacente, se for anulável. Isso é muito útil e você vê padrões semelhantesActivator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)
. É como umaas
palavra-chave, verifica o elenco e também o resultado do retorno. Se você deseja obter o tipo subjacente de anulável, fazer umaGetGenericTypeDefinition
verificação e obter o tipo genérico será uma má idéia. TambémGetUnderlyingType
é muito mais legível e memorável. Eu não me importaria se eu estivesse fazendo isso apenas ~ 1000 vezes.Esta versão:
:
fonte
Aqui está o que eu criei, pois tudo parecia falhar - pelo menos no PLC - Biblioteca de Classes Portátil / .NET Core com> = C # 6
Solução: estenda os métodos estáticos para qualquer tipo
T
eNullable<T>
use o fato de que o método de extensão estática, correspondente ao tipo subjacente, será invocado e terá precedência sobre o genéricoT
método de extensão .Para
T
:e para
Nullable<T>
Usar Reflection e
type.IsGenericType
... não funcionou no meu conjunto atual de .NET Runtimes. A documentação do MSDN também não ajudou.if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}
Em parte porque a API do Reflection foi alterada significativamente no .NET Core.
fonte
Eu acho que aqueles que usam os testes sugeridos pela Microsoft
IsGenericType
são bons, mas no código paraGetUnderlyingType
, a Microsoft usa um teste adicional para garantir que você não passou na definição de tipo genéricoNullable<>
:fonte
uma maneira simples de fazer isso:
estes são meus testes de unidade e todos passaram
testes unitários reais
fonte