Como verificar se um objeto é anulável?

202

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.

Autodidata
fonte
O código deve incluir cadeias de caracteres como anuláveis? Eles são um ValueType não genérico que parece ser anulável. Ou eles não são um ValueType?
TamusJRoyce 2/12/12
String não é um ValueType. É um tipo de referência.
Suncat2000
Esta é realmente uma boa pergunta! O 'Type.IsNullableType ()' é meio enganoso, porque na verdade só verifica se o tipo é um 'Nullable <T>', que não retornou os resultados esperados se você realmente quisesse verificar se existem tipos que possam aceitar um valor nulo. valor (por exemplo, tentei usar com a.IsNullableType (), em que 'a' era um 'typeof (string)' determinado em tempo de execução)
ErrCode 7/11/18
A resposta está em fieldInfo.FieldType: verifique se FieldType é genérico e o tipo genérico é do tipo Nullable <>. (Exemplo: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). Não tente obter obj.GetType (), pois a variável T de UndullingSystemType da Nullable <T> (no seu caso do tipo Boolean, em vez de Nullable <Boolean>), é um problema de boxe.
SoLaR 21/08/19

Respostas:

271

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 uso objpuramente do parâmetro para inferência de tipo genérica (para facilitar a chamada) - funcionaria quase de forma idêntica sem o objparâmetro.

static bool IsNullable<T>(T obj)
{
    if (obj == null) return true; // obvious
    Type type = typeof(T);
    if (!type.IsValueType) return true; // ref-type
    if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
    return false; // value-type
}

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

Marc Gravell
fonte
7
A última linha só é válida se você conseguir obter um Nullable <T> in a box em vez de ir direto para T. É possível, mas difícil de conseguir com o que me lembro.
Jon Skeet
Esse código foi útil para mim, não porque eu recebi um Nullable <T> in a box, mas porque estava escrevendo uma classe base do conversor WPF genérico e algumas propriedades são anuláveis, então usei o Nullable.GetUnderlyingType para detectar esse caso e o Activator.CreateInstance para criar um anulável em caixa, (Convert.ChangeType não trata anuláveis ​​btw).
Qwertie
1
@ Abel, se você quer dizer sua edição para esclarecer que ele não considerou tipos de referência, acho que minha resposta antecedeu essa edição; o leitor pode tomar sua própria decisão lá, com base em suas próprias necessidades, desconfio (confirmado: seu comentário é ref-type como adicionado às 14:42; minha resposta foi tudo <= 14:34)
Marc Gravell
1
Irá (obj == null) lançar uma exceção quando obj = 1?
Qi Fan
3
@JustinMorgan If Té um parâmetro genérico restrito por T : struct, então Tnão é permitido Nullable<>, portanto você não precisa verificar nesse caso! Eu sei que o tipo Nullable<>é uma estrutura, mas em C # a restrição where T : structexclui 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".
Jeppe Stig Nielsen
46

Existe uma solução muito simples usando sobrecargas de método

http://deanchalk.com/is-it-nullable/

excerto:

public static class ValueTypeHelper
{
    public static bool IsNullable<T>(T t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

então

static void Main(string[] args)
{
    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    bool result1 = ValueTypeHelper.IsNullable(a); // false
    bool result2 = ValueTypeHelper.IsNullable(b); // true
    bool result3 = ValueTypeHelper.IsNullable(c); // false
    bool result4 = ValueTypeHelper.IsNullable(d); // false
    bool result5 = ValueTypeHelper.IsNullable(e); // true
    bool result6 = ValueTypeHelper.IsNullable(f); // true
Dean Chalk
fonte
7
mais um para você, senhor, por adicionar casos de teste. Eu usei esses casos de teste para verificar todas as outras respostas. Mais pessoas devem ir um pouco mais.
Marty Neal
4
Pelo que vale, isso não funciona no VB.NET. Isso resulta em um erro do compilador de " Falha na resolução da sobrecarga porque nenhum 'IsNullable' acessível é mais específico para esses argumentos " em todas as situações em Trueque seria retornado.
Ckittel
1
Eu realmente gosto dessa solução - e é uma pena que o VB não possa lidar com isso. Eu tentei trabalhar com o ValueType, mas tive problemas com o compilador VB sendo inconsistente sobre qual sobrecarga usar com base no fato de ser chamado como um método compartilhado ou uma extensão, até levantei uma pergunta sobre isso, pois parece estranho: stackoverflow.com/ perguntas / 12319591 /…
James Close
22
Você está verificando o tipo de tempo de compilação , mas já é óbvio (do intellisense) se o tipo de tempo de compilação é anulável ( System.Nullable<>). Se você diz object g = e;e depois ValueTypeHelper.IsNullable(g), o que espera obter?
Jeppe Stig Nielsen
18
Acabei de verificar; isso não funciona , como Jeppe disse. Se as variáveis ​​forem convertidas em objeto, sempre retornará false. Portanto, você não pode determinar o tipo de um objeto desconhecido no tempo de execução dessa maneira. A única vez que isso funciona é se o tipo for corrigido no tempo de compilação e, nesse caso, você não precisará de uma verificação de tempo de execução.
HugoRune 02/07
30

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>um Nullable<>?", mas também "é List<int>um List<>?".

A maioria das soluções fornecidas usa o Nullable.GetUnderlyingType()método, que obviamente funcionará apenas com o caso de Nullable<>. 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 exemplo Nullable<int>, na definição de tipo genérico Nullable<>,. Você pode fazer isso usando o GetGenericTypeDefinition()método da Typeclasse Você pode comparar o tipo resultante com Nullable<>:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

O mesmo pode ser aplicado a qualquer tipo genérico:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

Vários tipos podem parecer iguais, mas um número diferente de argumentos de tipo significa que é um tipo completamente diferente.

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

Como os Typeobjetos 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:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

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:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}
Allon Guralnek
fonte
@ AllonGuralnek Existe uma versão simplificada lá em baixo na minha resposta. Eu queria fazê-lo como edição e, como minha reputação não é o seu nível, seria editada sem o meu nome na sua resposta, mesmo assim, parece que a revisão está sempre me dando um tiro na perna, que é um autor que adere mesmo que fosse não. Mundo estranho, algumas pessoas não conseguem definições :).
Ipavlu
@ ipavlu: Sua versão não é simplificada, é de fato mais complicada. Eu acho que você quer dizer que está otimizado desde que você armazena em cache o resultado. Isso torna mais difícil de entender.
Allon Guralnek
@ Classe genérica estática AllonGuralnek e campos estáticos de inicialização única, isso é complicado? Meu Deus, cometi um crime terrível :).
Ipavlu 11/11/2015
@ipavku: Sim, porque não tem nada a ver com a pergunta "Como verificar se um objeto é anulável?". Eu tento manter as coisas simples e objetivas, e evito introduzir conceitos desnecessários e não relacionados.
Allon Guralnek 11/11/2015
1
@nawfal: Se eu entendi corretamente, você está buscando minha implementação em face da existência da 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 (substituindo typeof(Nullable<>)por qualquer outro tipo). Se você olhar para as fontes de GetUnderlyingType()(original ou descompilado), verá que é muito semelhante ao meu código.
Allon Guralnek
30

Isso funciona para mim e parece simples:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

Para tipos de valor:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}
Erik
fonte
7
Por que vale a pena, este também é o teste usado pela Microsoft
canton7
1
Bom ... Essa não é a resposta principal porque veio mais tarde? Acho a resposta principal tão confusa.
Vincent Buscarello
1
Essa deve ser a resposta principal. Depois de dias de tentar métodos diferentes I aleatoriamente pensou desta solução, tentou fazê-lo, e ele parece estar funcionando perfeitamente (em comparação com a resposta mais votadas)
user3163495
2
Essa é uma excelente solução para descobrir se alguma instância pode ser definida como NULL, mas retornará true para tudo o que puder ser definido como null, incluindo objetos comuns. É importante perceber que a pergunta original queria especificamente detectar Nullable ValueTypes.
precisa saber é o seguinte
20

Bem, você poderia usar:

return !(o is ValueType);

... mas um objeto em si não é anulável ou não - um tipo é. Como você estava planejando usar isso?

Jon Skeet
fonte
2
Isso me assustou um pouco. por exemplo, int? i = 5; typeof (i) retorna System.Int32 em vez de Nullable <Int32> - typeof (int?) retorna Nullable <Int32> .. onde posso obter alguma clareza sobre este tópico?
Gishu
2
typeof (i) gera um erro do compilador - você não pode usar typeof com uma variável. O que você realmente fez?
9139 Jon Skeet
15
i.GetType () colocará a caixa em Object primeiro e não existe um tipo nulo em caixa - Nullable <int> é encaixotado em uma referência nula ou em um int em caixa.
11139 Jon Skeet
Dessa maneira, é melhor que Nullable.GetUnderlyingType (type)! = Null?
Kiquenet
@Kiquenet: Não temos o tipo aqui - apenas o valor.
precisa
11

A maneira mais simples de descobrir é:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
CARLOS LOTH
fonte
+1. Excelente solução para tipos com capacidade nula in a box. Ainda não testei isso especificamente. Portanto, se alguém puder verificar, isso seria apreciado.
TamusJRoyce
Eu já testei. Eu tive que criar um tipo de Nullabletipo, mas com semântica diferente. Na minha situação, devo apoiar nullcomo um valor válido e também não apoiar nenhum valor. Então, um criou um Optionaltipo. Como era necessário oferecer suporte a nullvalores, eu também tive que implementar código para manipular Nullablevalores como parte da minha implementação. É daí que esse código veio.
CARLOS LOTH
9
Eu acho que esta solução está errada. Passar um tipo de valor Nullable como argumento para um método que espera um parâmetro do objeto de tipo deve causar o encaixotamento. Anulável é um tipo de valor e o resultado da conversão de boxe é um tipo de referência. Não há anuláveis ​​em caixa. Eu acredito que esse método sempre retorna falso?
Mishax #
1
Algum teste sobre isso como outras respostas?
Kiquenet
5
Não funciona por causa do valor do boxe. Ele sempre retornará FALSO.
N Rocking
10

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:

    public static bool IsObjectNullable<T>(T obj)
    {
        // If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
        if (!typeof(T).IsValueType || obj == null)
            return true;

        // Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
        return false; 
    }

    public static bool IsObjectNullable<T>(T? obj) where T : struct
    {
        // Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
        return true;
    }

E aqui está minha modificação no código de teste do cliente para a solução acima:

    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    string g = "something";

    bool isnullable = IsObjectNullable(a); // false 
    isnullable = IsObjectNullable(b); // true 
    isnullable = IsObjectNullable(c); // true 
    isnullable = IsObjectNullable(d); // true 
    isnullable = IsObjectNullable(e); // true 
    isnullable = IsObjectNullable(f); // true 
    isnullable = IsObjectNullable(g); // true

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:

    public static bool IsObjectNullable<T>(T obj)
    {
        Type argType = typeof(T);
        if (!argType.IsValueType || obj == null)
            return true;
        return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

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.

Mark Jones
fonte
8

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:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Isso pode ser chamado assim:

bool isNullable = typeof(int).IsNullable();

Isso também parece uma maneira lógica de acessar, IsNullable()porque se encaixa em todos os outros IsXxxx()métodos da Typeclasse.

sclarke81
fonte
1
Você não queria usar "==" em vez de "! ="?
vkelman
Bom ponto @vkelman Em vez de fazer essa alteração, atualizei a resposta para usar a sugestão atual da Microsoft, pois isso mudou desde que escrevi isso.
sclarke81
6

Tenha cuidado, ao encaixar um tipo anulável ( Nullable<int>ou int? Por exemplo):

int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);

int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))

Torna-se um tipo de referência verdadeiro, para que você perca o fato de ser anulável.

thinkbeforecoding
fonte
3

Talvez um pouco fora de tópico, mas ainda algumas informações interessantes. Eu encontro muitas pessoas que usam Nullable.GetUnderlyingType() != nullpara identificar se um tipo é anulável. Obviamente, isso funciona, mas a Microsoft recomenda o seguinte type.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 ...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
    {
        Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
    }

    Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
   {
       Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
   }

   Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
   stopwatch.Stop();
}
Roel van Megen
fonte
1
Olá, provavelmente existe um problema com a medição do tempo. A declaração pode afetar os resultados. Você testou sem Assert? O Console.WriteLine também deve estar fora da área medida. +1 para uma tentativa de quantificar problemas de desempenho :).
Ipavlu 11/11/2015
@ipavlu Console.WriteLineé de facto fora da área doseada;)
Nawfal
Roel, como o ipavlu mencionou, Assertdeve 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 segundos GetUnderlyingTypee ~ 1 segundo por GetGenericTypeDefinition, ou seja, GetGenericTypeDefinitioné duas vezes mais rápido (não três vezes).
Nawfal #
Fiz outra rodada com 2 nulos e 2 nulos - desta vez GetUnderlyingTypefoi 2,5 vezes mais lento. Com apenas não nulos - desta vez, ambos são pescoço e pescoço.
Nawfal #
Mais importante, porém, 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 semelhantes Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). É como uma aspalavra-chave, verifica o elenco e também o resultado do retorno. Se você deseja obter o tipo subjacente de anulável, fazer uma GetGenericTypeDefinitionverificação e obter o tipo genérico será uma má idéia. Também GetUnderlyingTypeé muito mais legível e memorável. Eu não me importaria se eu estivesse fazendo isso apenas ~ 1000 vezes.
Nawfal #
0

Esta versão:

  • os resultados do armazenamento em cache são mais rápidos,
  • não requer variáveis ​​desnecessárias, como Method (T obj)
  • NÃO É COMPLICADO :),
  • apenas classe genérica estática, que possui campos calculados uma vez

:

public static class IsNullable<T>
{
    private static readonly Type type = typeof(T);
    private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    public static bool Result { get { return is_nullable; } }
}

bool is_nullable = IsNullable<int?>.Result;
ipavlu
fonte
Eu acho que você respondeu a si mesmo com a declaração estática 'is_nullable'. Dica: declarar objetos com int? (objeto a = (int?) 8;) e veja o que acontece.
SoLaR 21/08/19
0

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 Te Nullable<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:

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

e para Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

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.

Lorenz Lo Sauer
fonte
0

Eu acho que aqueles que usam os testes sugeridos pela Microsoft IsGenericTypesão bons, mas no código para GetUnderlyingType, a Microsoft usa um teste adicional para garantir que você não passou na definição de tipo genérico Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
NetMage
fonte
-1

uma maneira simples de fazer isso:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

estes são meus testes de unidade e todos passaram

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

testes unitários reais

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }
VJPPaz
fonte