Come verificare se un oggetto è nullable?


203

Come posso verificare se un determinato oggetto è nullable in altre parole come implementare il seguente metodo ...

bool IsNullableValueType(object o)
{
    ...
}

EDIT: sto cercando tipi di valore nullable. Non avevo in mente i tipi di riferimento.

//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 ora si riferisce a un oggetto di tipo bool( System.Boolean) con valore uguale a true. Quello che volevo davvero era un oggetto di tipoNullable<bool>

Così ora come soluzione ho deciso di verificare se o è nullable e creare un wrapper nullable attorno a obj.


Il codice dovrebbe includere stringhe come nullable? Sono un ValueType non generico che sembra essere nullable. O non sono un ValueType?
TamusJRoyce,

String non è un ValueType. È un tipo di riferimento.
Suncat2000,

Questa è davvero una buona domanda! "Type.IsNullableType ()" è un po 'ingannevole perché in realtà controlla solo che il tipo sia un "Nullable <T>", che non ha restituito i risultati previsti se si voleva effettivamente verificare la presenza di eventuali tipi che possono accettare un valore nullo valore (ad esempio, ho provato a utilizzare con a.IsNullableType (), dove 'a' era un 'tipoof (stringa)' determinato in fase di esecuzione)
ErrCode

La risposta è in fieldInfo.FieldType: controlla se FieldType è generico e il tipo generico è di tipo Nullable <>. (Esempio: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). Non tentare di ottenere obj.GetType () avrà UndelyingSystemType della variabile T nullabile <T> (nel tuo caso di tipo booleano, anziché Nullable <Boolean>), è un problema di boxe.
SoLaR,

Risposte:


272

Esistono due tipi di nullable Nullable<T>e di riferimento.

Jon mi ha corretto che è difficile ottenere il tipo se inscatolato, ma è possibile con generici: - quindi che ne dici di seguito. In realtà si tratta di testare il tipo T, ma usando il objparametro esclusivamente per l'inferenza di tipo generico (per rendere più semplice la chiamata) - funzionerebbe quasi identicamente senza il objparametro, però.

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
}

Ma questo non funzionerà così bene se hai già inscatolato il valore su una variabile oggetto.

Documentazione Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type


7
L'ultima riga è valida solo se in qualche modo riesci a ottenere una Nullable inscatolata <T> invece di inscatolare direttamente a T. È possibile, ma difficile da ottenere da ciò che ricordo.
Jon Skeet,

Questo codice mi è stato utile, non perché ho ottenuto un Nullable in scatola <T> ma perché stavo scrivendo una classe di base del convertitore WPF generico e alcune proprietà sono nullable, quindi ho usato Nullable.GetUnderlyingType per rilevare quel caso e Activator.CreateInstance per creare un nullable inscatolato, (Convert.ChangeType non gestisce nullable tra l'altro).
Qwertie,

1
@Abel se intendi la sua modifica per chiarire che non aveva preso in considerazione i tipi di riferimento, penso che la mia risposta sia precedente a quella modifica; il lettore può prendere la propria decisione lì, in base alle proprie esigenze, sospetto (confermato: il suo commento è di ri-tipo come aggiunto alle 14:42; la mia risposta è stata tutta <= 14:34)
Marc Gravell

1
(Obj == null) genererà un'eccezione quando obj = 1?
Qi Fan

3
@JustinMorgan Se Tè un parametro generico vincolato da T : struct, allora Tnon è permesso Nullable<>, quindi non è necessario un controllo in questo caso! So che il tipo Nullable<>è una struttura, ma in C # il vincolo where T : structesclude specificamente tipi di valore nullable. La specifica dice: "Notare che sebbene classificato come un tipo di valore, un tipo nullable (§4.1.10) non soddisfa il vincolo del tipo di valore."
Jeppe Stig Nielsen,

46

Esiste una soluzione molto semplice che utilizza sovraccarichi di metodo

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

estratto:

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; }
}

poi

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

7
più uno per te signore per l'aggiunta di casi di test. Ho usato quei casi di test per controllare tutte le altre risposte. Altre persone dovrebbero andare questo extra.
Marty Neal,

4
Per quello che vale, questo non funziona in VB.NET. Il risultato è un errore del compilatore di " Risoluzione del sovraccarico non riuscita perché nessuna" IsNullable "accessibile è più specifica per questi argomenti " in tutte le situazioni in cui Trueverrebbe restituita.
ckittel,

1
Mi piace molto questa soluzione - ed è un peccato che VB non riesca a gestirla. Ho provato a lavorare con ValueType ma ho avuto problemi con il compilatore VB incoerente su quale sovraccarico usare in base al fatto che fosse chiamato come metodo condiviso o estensione, ho anche sollevato una domanda su questo perché sembra strano: stackoverflow.com/ domande / 12319591 /…
James Close

22
Stai controllando il tipo di tempo di compilazione , ma è già ovvio (da intellisense) se il tipo di tempo di compilazione è nullable ( System.Nullable<>). Se dici object g = e;e poi ValueTypeHelper.IsNullable(g), cosa ti aspetti di ottenere?
Jeppe Stig Nielsen,

18
Ho appena verificato; questo non funziona , come diceva Jeppe. Se le variabili vengono trasmesse all'oggetto, restituirà sempre false. Quindi non è possibile determinare il tipo di un oggetto sconosciuto in fase di esecuzione in questo modo. L'unica volta che funziona è se il tipo è riparato in fase di compilazione e in tal caso non è necessario un controllo di runtime.
HugoRune,

30

La domanda di "Come verificare se un tipo è nullable?" è in realtà "Come verificare se un tipo è Nullable<>?", che può essere generalizzato a "Come verificare se un tipo è un tipo costruito di un tipo generico?", in modo che non risponda solo alla domanda "È Nullable<int>un Nullable<>?", ma anche "È List<int>un List<>?".

La maggior parte della soluzione fornita utilizza il Nullable.GetUnderlyingType()metodo, che ovviamente funzionerà solo con il caso di Nullable<>. Non ho visto la soluzione riflettente generale che funzionerà con qualsiasi tipo generico, quindi ho deciso di aggiungerla qui per i posteri, anche se questa domanda ha già ricevuto risposta molto tempo fa.

Per verificare se un tipo è una qualche forma di Nullable<>utilizzando la riflessione, è necessario prima convertire il tipo generico costruito, per esempio Nullable<int>, nella definizione di tipo generico, Nullable<>. Puoi farlo usando il GetGenericTypeDefinition()metodo della Typeclasse. È quindi possibile confrontare il tipo risultante con Nullable<>:

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

Lo stesso può essere applicato a qualsiasi tipo generico:

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

Diversi tipi possono sembrare uguali, ma un diverso numero di argomenti di tipo significa che è un tipo completamente diverso.

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

Poiché gli Typeoggetti vengono istanziati una volta per tipo, è possibile verificare la parità di riferimento tra di essi. Quindi, se si desidera verificare se due oggetti hanno la stessa definizione di tipo generico, è possibile scrivere:

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

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

Se desideri verificare se un oggetto è nullable, anziché a Type, puoi utilizzare la tecnica sopra descritta insieme alla soluzione di Marc Gravell per creare un metodo piuttosto semplice:

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

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}

@ AllonGuralnek C'è una versione semplificata laggiù nella mia risposta. Volevo renderlo come una modifica e poiché la mia reputazione non è il tuo livello, sarebbe una modifica senza il mio nome sulla tua risposta, anche così, sembra che la recensione mi stia sempre prendendo in giro, che stia maleducando l'autore anche se lo fosse non. Strano mondo, alcune persone non ottengono definizioni :).
ipavlu,

@ipavlu: la tua versione non è semplificata, in realtà è più complicata. Penso che tu intenda che è ottimizzato poiché memorizzi nella cache il risultato. Ciò rende più difficile la comprensione.
Allon Guralnek,

@ AllonGuralnek classe generica statica e campi inizializzati statici una volta, è complicato? Caro Dio, ho commesso un crimine terribile :).
ipavlu,

@ipavku: Sì, perché non ha nulla a che fare con la domanda "Come verificare se un oggetto è nullable?". Cerco di mantenerlo semplice e puntuale, ed evito di introdurre concetti non necessari e non correlati.
Allon Guralnek,

1
@nawfal: se ti ho capito correttamente, stai cercando la mia implementazione di fronte all'esistenza di Nullable.GetUnderlyingType()quella già fornita dal framework. Perché non usare semplicemente il metodo nel framework? Bene, dovresti. È più chiaro, più conciso e testato meglio. Ma nel mio post sto cercando di insegnare come utilizzare la riflessione per ottenere le informazioni desiderate, in modo che qualcuno possa applicarle a qualsiasi tipo (sostituendolo typeof(Nullable<>)con qualsiasi altro tipo). Se guardi le fonti di GetUnderlyingType()(originale o decompilato), vedrai che è molto simile al mio codice.
Allon Guralnek,

30

Questo funziona per me e sembra semplice:

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

Per i tipi di valore:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}

7
Per quello che vale, questo è anche il test utilizzato da Microsoft
canton7,

1
Bello ... Non è questa la risposta migliore perché è arrivata dopo? Trovo la risposta migliore così confusa.
Vincent Buscarello,

1
Questa dovrebbe essere la risposta migliore. Dopo giorni in cui ho provato diversi metodi ho pensato a caso a questa soluzione, l'ho provata e sembra funzionare perfettamente (rispetto alla risposta più votata)
user3163495

2
Questa è un'ottima soluzione per scoprire se qualsiasi istanza può essere impostata su NULL, ma tornerà vera per tutto ciò che può essere impostato su null, inclusi gli oggetti ordinari. È importante rendersi conto che la domanda originale voleva specificamente rilevare Nullable ValueTypes.
JamesHoux,

20

Bene, potresti usare:

return !(o is ValueType);

... ma un oggetto in sé non è nulla o altrimenti - un tipo lo è. Come stavi pensando di usarlo?


2
Questo mi ha gettato un po '. ad esempio int? i = 5; typeof (i) restituisce System.Int32 invece di Nullable <Int32> - typeof (int?) restituisce Nullable <Int32> .. dove posso trovare un po 'di chiarezza su questo argomento?
Gishu,

2
typeof (i) genererà un errore del compilatore: non è possibile utilizzare typeof con una variabile. Che cosa hai effettivamente fatto?
Jon Skeet,

15
i.GetType () eseguirà prima la box su Object e non esiste un tipo nullable in box: Nullable <int> viene inscatolato in riferimento null o int int box.
Jon Skeet,

In questo modo è meglio di Nullable.GetUnderlyingType (type)! = Null?
Kiquenet,

@Kiquenet: non abbiamo il tipo qui - solo il valore.
Jon Skeet,

11

Il modo più semplice che posso capire è:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

+1. Ottima soluzione per i tipi null-boxed. Non l'ho ancora testato in modo specifico. Quindi, se qualcun altro può verificare, sarebbe apprezzato.
TamusJRoyce,

L'ho già provato. Ho dovuto creare un tipo di Nullabletipo, ma con semantica diversa. Nella mia situazione dovrei supportare nullcome valore valido e anche non supportare alcun valore. Quindi un creato un Optionaltipo. Poiché era necessario supportare i nullvalori, dovevo anche implementare il codice per la gestione dei Nullablevalori come parte della mia implementazione. Ecco da dove proviene questo codice.
CARLOS LOTH

9
Penso che questa soluzione sia sbagliata. Il passaggio di un tipo di valore Nullable come argomento a un metodo in attesa di un parametro di tipo oggetto dovrebbe causare l'inscatolamento. Nullable è un tipo di valore e il risultato della conversione di boxe è un tipo di riferimento. Non ci sono nullable inscatolati. Credo che questo metodo ritorni sempre falso?
Mishax,

1
Qualche prova al riguardo come un'altra risposta?
Kiquenet,

5
Non funziona a causa del valore di boxe. Restituirà sempre FALSO.
N Rocking

10

Ci sono due problemi qui: 1) test per vedere se un Tipo è nullable; e 2) test per vedere se un oggetto rappresenta un Tipo nullable.

Per il numero 1 (test di un tipo), ecco una soluzione che ho usato nei miei sistemi: TypeIsNullable-check solution

Per il numero 2 (test di un oggetto), la soluzione di Dean Chalk sopra funziona per i tipi di valore, ma non funziona per i tipi di riferimento, poiché l'utilizzo del sovraccarico <T> restituisce sempre false. Poiché i tipi di riferimento sono intrinsecamente nulla, il test di un tipo di riferimento dovrebbe sempre restituire true. Si prega di vedere la nota [A proposito di "nullability"] sotto per una spiegazione di queste semantiche. Quindi, ecco la mia modifica all'approccio di 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;
    }

Ed ecco la mia modifica al codice di test client per la soluzione sopra:

    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

Il motivo per cui ho modificato l'approccio di Dean in IsObjectNullable <T> (T t) è che il suo approccio originale ha sempre restituito false per un tipo di riferimento. Poiché un metodo come IsObjectNullable dovrebbe essere in grado di gestire i valori del tipo di riferimento e poiché tutti i tipi di riferimento sono intrinsecamente nulli, quindi se viene passato un tipo di riferimento o un valore nullo, il metodo dovrebbe sempre restituire true.

I due metodi precedenti potrebbero essere sostituiti con il seguente metodo singolo e ottenere lo stesso risultato:

    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<>);
    }

Tuttavia, il problema con quest'ultimo approccio a metodo singolo è che le prestazioni risentono dell'utilizzo di un parametro Nullable <T>. Ci vuole molto più tempo del processore per eseguire l'ultima riga di questo singolo metodo di quanto non consenta al compilatore di scegliere il secondo sovraccarico del metodo mostrato in precedenza quando nella chiamata IsObjectNullable viene utilizzato un parametro di tipo Nullable <T>. Pertanto, la soluzione ottimale è utilizzare l'approccio a due metodi qui illustrato.

CAVEAT: questo metodo funziona in modo affidabile solo se chiamato usando il riferimento all'oggetto originale o una copia esatta, come mostrato negli esempi. Tuttavia, se un oggetto nullable è inscatolato in un altro tipo (come oggetto, ecc.) Invece di rimanere nella sua forma Nullable <> originale, questo metodo non funzionerà in modo affidabile. Se il codice che chiama questo metodo non utilizza il riferimento all'oggetto originale, senza scatola o una copia esatta, non può determinare in modo affidabile la nullità dell'oggetto utilizzando questo metodo.

Nella maggior parte degli scenari di codifica, per determinare la nullità si deve invece fare affidamento sul test del Tipo dell'oggetto originale, non sul suo riferimento (ad esempio, il codice deve avere accesso al Tipo originale dell'oggetto per determinare il nullabilità). In questi casi più comuni, IsTypeNullable (vedi link) è un metodo affidabile per determinare la nullità.

PS - A proposito di "nullability"

Dovrei ripetere una dichiarazione sulla nullità che ho fatto in un post separato, che si applica direttamente per affrontare correttamente questo argomento. Cioè, credo che il focus della discussione qui non dovrebbe essere come verificare se un oggetto è un tipo Nullable generico, ma piuttosto se si può assegnare un valore null a un oggetto del suo tipo. In altre parole, penso che dovremmo determinare se un tipo di oggetto è nullable, non se sia Nullable. La differenza sta nella semantica, vale a dire i motivi pratici per determinare la nullabilità, che di solito è tutto ciò che conta.

In un sistema che utilizza oggetti con tipi probabilmente sconosciuti fino al runtime (servizi Web, chiamate remote, database, feed, ecc.), Un requisito comune è determinare se un oggetto null può essere assegnato all'oggetto o se l'oggetto potrebbe contenere un null. L'esecuzione di tali operazioni su tipi non annullabili produrrà probabilmente errori, in genere eccezioni, che sono molto costosi sia in termini di prestazioni che di requisiti di codifica. Per adottare l'approccio altamente preferito di evitare proattivamente tali problemi, è necessario determinare se un oggetto di un tipo arbitrario è in grado di contenere un valore nullo; vale a dire, se è generalmente "nullable".

In un senso molto pratico e tipico, la nullabilità in termini di .NET non implica necessariamente che il Tipo di un oggetto sia una forma di Nullable. In molti casi, infatti, gli oggetti hanno tipi di riferimento, possono contenere un valore nullo e quindi sono tutti nullable; nessuno di questi ha un tipo Nullable. Pertanto, per scopi pratici nella maggior parte degli scenari, i test dovrebbero essere eseguiti per il concetto generale di nullability, rispetto al concetto dipendente dall'implementazione di Nullable. Quindi non dovremmo essere bloccati concentrandoci esclusivamente sul tipo Nullable .NET, ma piuttosto incorporare la nostra comprensione dei suoi requisiti e comportamenti nel processo di focalizzazione sul concetto generale e pratico di nullability.


8

La soluzione più semplice che mi è venuta in mente è quella di implementare la soluzione di Microsoft ( come: identificare un tipo nullable (Guida per programmatori C #) ) come metodo di estensione:

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

Questo può quindi essere chiamato così:

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

Questo sembra anche un modo logico per accedere IsNullable()perché si adatta a tutti gli altri IsXxxx()metodi della Typeclasse.


1
Non volevi usare "==" invece di "! ="?
vkelman,

Good spot @vkelman Invece di apportare questa modifica, ho aggiornato la risposta per utilizzare l'attuale suggerimento di Microsoft poiché questo è cambiato da quando l'ho scritto.
sclarke81

6

Fai attenzione, quando inscatoli un tipo nullable ( Nullable<int>o int? Per esempio):

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

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

Diventa un vero tipo di riferimento, quindi perdi il fatto che fosse nullable.


3

Forse un po 'fuori tema, ma comunque alcune informazioni interessanti. Trovo molte persone che usano Nullable.GetUnderlyingType() != nullper identificare se un tipo è nullable. Questo ovviamente funziona, ma Microsoft consiglia quanto segue type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(vedere http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

Ho osservato questo aspetto dal punto di vista delle prestazioni. La conclusione del test (un milione di tentativi) di seguito è che quando un tipo è nullable, l'opzione Microsoft offre le migliori prestazioni.

Nullable.GetUnderlyingType (): 1335ms (3 volte più lento)

GetGenericTypeDefinition () == typeof (Nullable <>): 500ms

So che stiamo parlando di un piccolo lasso di tempo, ma a tutti piace modificare i millisecondi :-)! Quindi se il tuo capo vuole che tu riduca alcuni millisecondi, questo è il tuo salvatore ...

/// <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();
}

1
Salve, c'è probabilmente un problema con la misurazione del tempo, l'Assert può influire sui risultati. Hai provato senza Assert? Anche Console.WriteLine dovrebbe essere al di fuori dell'area misurata. +1 per un tentativo di quantificare i problemi di prestazioni :).
ipavlu,

@ipavlu Console.WriteLineè effettivamente al di fuori dell'area misurata;)
nawfal

Roel, come ha detto ipavlu, Assertdovrebbe essere al di fuori del ciclo. In secondo luogo, dovresti anche testarlo su non annullabili e testare casi falsi. Ho fatto un test simile (2 nulables e 4 non nullable) e ottengo ~ 2 secondi per GetUnderlyingTypee ~ 1 secondo per GetGenericTypeDefinition, cioè GetGenericTypeDefinitionè due volte più veloce (non tre volte).
nawfal,

Ha fatto un altro giro con 2 nullable e 2 non nullable - questa volta è GetUnderlyingTypestato 2,5 volte più lento. Con solo non nullable - questa volta sono entrambi collo e collo.
nawfal,

Ma soprattutto, GetUnderlyingTypeè utile quando devi controllare la nullità e ottenere il tipo sottostante se è nullable. Questo è molto utile e vedi modelli spesso simili Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). È come una asparola chiave, controlla il cast e lo fa e restituisce il risultato. Se si desidera ripristinare il tipo di nullable sottostante, fare una GetGenericTypeDefinitionverifica e ottenere un tipo generico sarà una cattiva idea. Inoltre GetUnderlyingTypeè molto più leggibile e memorabile. Non mi dispiacerebbe se lo facessi solo ~ 1000 volte.
nawfal,

0

Questa versione:

  • i risultati della memorizzazione nella cache sono più veloci,
  • non richiede variabili non necessarie, come Metodo (T obj)
  • NON COMPLICATO :),
  • solo una classe generica statica, che ha campi calcolati una volta

:

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;

Penso che ti sia risposto con quella dichiarazione statica 'is_nullable'. Suggerimento: dichiarare oggetti con int? (oggetto a = (int?) 8;) e guarda cosa succede.
SoLaR,

0

Ecco cosa mi è venuto in mente, dato che tutto il resto sembrava fallire - almeno sul PLC - Portable Class Library / .NET Core con> = C # 6

Soluzione: estendere i metodi statici per qualsiasi tipo Te Nullable<T>utilizzare il fatto che il metodo di estensione statica, corrispondente al tipo sottostante, verrà richiamato e abbia la precedenza sul Tmetodo di estensione generico .

Per T :

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

e per Nullable<T>

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

L'uso di Reflection e type.IsGenericType... non ha funzionato sul mio set corrente di .NET Runtimes. Nemmeno il documentazione MSDN ha aiutato.

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

In parte perché l'API Reflection è stata modificata in modo abbastanza significativo in .NET Core.


0

Penso che quelli che usano i test suggeriti da Microsoft contro IsGenericTypesiano buoni, ma nel codice per GetUnderlyingType, Microsoft usa un test aggiuntivo per assicurarsi di non aver passato la definizione di tipo generico Nullable<>:

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

-1

un modo semplice per farlo:

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

        return true;
    }

questi sono i miei test unitari e tutti hanno superato

    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

test unitari effettivi

    [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);
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.