Come verificare se un object è annullabile?

Come faccio a verificare se un dato object è annullabile in altre parole come implementare il seguente metodo …

bool IsNullableValueType(object o) { ... } 

EDIT: Sto cercando i tipi di valori nullable. Non avevo in mente tipi di ref.

 //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 fa riferimento a un object di tipo bool ( System.Boolean ) con valore uguale a true . Quello che volevo veramente era un object di tipo Nullable

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

Esistono due tipi di nullable – Nullable e tipo di riferimento.

Jon mi ha corretto che è difficile ottenere il tipo se messo in scatola, ma puoi farlo con i generici: – allora, che ne dici di seguito. Questo in realtà sta testando il tipo T , ma usando il parametro obj puramente per inferenza di tipo generico (per renderlo facile da chiamare) – funzionerebbe quasi identicamente senza il parametro obj , però.

 static bool IsNullable(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 return false; // value-type } 

Ma questo non funzionerà molto bene se hai già inserito il valore in una variabile object.

C’è una soluzione molto semplice che utilizza overload di metodi

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

estratto:

 public static class ValueTypeHelper { public static bool IsNullable(T t) { return false; } public static bool IsNullable(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 

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

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

Per verificare se un tipo è una forma di Nullable<> utilizzando il reflection, devi prima convertire il tuo tipo generico costruito, ad esempio Nullable , nella definizione di tipo generico, Nullable<> . Puoi farlo usando il metodo GetGenericTypeDefinition() della class Type . È quindi ansible confrontare il tipo risultante con Nullable<> :

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

Lo stesso può essere applicato a qualsiasi tipo generico:

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

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

 Type typeToTest = typeof(Action); 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é l’object Type viene istanziato una volta per tipo, è ansible verificare l’uguaglianza di riferimento tra di essi. Quindi, se vuoi verificare se due oggetti hanno la stessa definizione di tipo generico, puoi scrivere:

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

Se vuoi verificare se un object è annullabile, piuttosto che un Type , puoi utilizzare la tecnica di cui sopra insieme alla soluzione di Marc Gravell per creare un metodo piuttosto semplice:

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

Bene, puoi usare:

 return !(o is ValueType); 

… ma un object stesso non è annullabile o altrimenti – un tipo è. Come pensavi di usarlo?

Questo funziona per me e sembra semplice:

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

Il modo più semplice che riesco a capire è:

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

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

Per il numero 1 (testare un tipo), ecco una soluzione che ho usato nei miei sistemi: Soluzione di controllo di tipo IsNullable

Per il problema 2 (testare un object), la soluzione di Dean Chalk sopra funziona per i tipi di valore, ma non funziona per i tipi di riferimento, poiché l’utilizzo dell’overload restituisce sempre false. Poiché i tipi di riferimento sono intrinsecamente nullable, il test di un tipo di riferimento deve sempre restituire true. Si prega di consultare la nota [Informazioni su “nullability”] di seguito per una spiegazione di queste semantiche. Quindi, ecco la mia modifica all’approccio di Dean:

  public static bool IsObjectNullable(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? 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 del client per la soluzione di cui 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) è 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 di tipo di riferimento e poiché tutti i tipi di riferimento sono intrinsecamente nullable, se viene passato un tipo di riferimento o un valore null, il metodo deve 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 obj) { Type argType = typeof(T); if (!argType.IsValueType || obj == null) return true; return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>); } 

Tuttavia, il problema con questo ultimo approccio a metodo singolo è che le prestazioni subiscono quando viene utilizzato un parametro Nullabile . È necessario molto più tempo del processore per eseguire l’ultima riga di questo singolo metodo piuttosto che per consentire al compilatore di scegliere il secondo overload di metodo mostrato in precedenza quando viene utilizzato un parametro Ntyble -type nella chiamata IsObjectNullable. Pertanto, la soluzione ottimale è utilizzare l’approccio a due metodi qui illustrato.

CAVEAT: questo metodo funziona in modo affidabile solo se viene chiamato utilizzando il riferimento all’object originale o una copia esatta, come mostrato negli esempi. Tuttavia, se un object nullable è inserito in un altro tipo (come object, ecc.) Invece di rimanere nel suo modulo Nullable <> originale, questo metodo non funzionerà in modo affidabile. Se il codice che chiama questo metodo non sta utilizzando il riferimento all’oggetto originale, unboxed o una copia esatta, non può determinare in modo affidabile il nullability dell’oggetto utilizzando questo metodo.

Nella maggior parte degli scenari di codifica, per determinare il nullability bisogna invece fare affidamento sul test del tipo dell’object originale, non sul suo riferimento (ad esempio, il codice deve avere accesso al tipo originale dell’object per determinare il nullability). In questi casi più comuni, IsTypeNullable (vedi collegamento) è un metodo affidabile per determinare il nullability.

PS – Informazioni su “nullability”

Dovrei ripetere una dichiarazione sul nullability 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 object è un tipo Nullable generico, ma piuttosto se si può assegnare un valore di null a un object del suo tipo. In altre parole, penso che dovremmo determinare se un tipo di object è annullabile, non se è Nullable. La differenza è nella semantica, vale a dire le ragioni pratiche per determinare il nullability, che di solito è tutto ciò che conta.

In un sistema che utilizza oggetti con tipi possibilmente sconosciuti fino al momento dell’esecuzione (servizi Web, chiamate remote, database, feed, ecc.), Un requisito comune è determinare se un object nullo può essere assegnato o se l’object potrebbe contenere un null. L’esecuzione di tali operazioni su tipi non annulli probabilmente produrrà errori, di solito eccezioni, che sono molto costosi in termini di prestazioni e requisiti di codifica. Per adottare l’approccio altamente preferito di evitare protriggersmente tali problemi, è necessario determinare se un object di un tipo arbitrario è in grado di contenere un valore nullo; cioè, se è generalmente ‘nullable’.

In un senso molto pratico e tipico, il nullability in termini .NET non implica necessariamente che il Type di un object sia una forma di Nullable. In molti casi, infatti, gli oggetti hanno tipi di riferimento, possono contenere un valore nullo e quindi sono annullabili; nessuno di questi ha un tipo Nullable. Pertanto, per scopi pratici nella maggior parte degli scenari, il test dovrebbe essere fatto per il concetto generale di nullability, rispetto al concetto di Nullable dipendente dall’implementazione. Quindi non dovremmo rimanere bloccati concentrandoci esclusivamente sul tipo .NET Nullable, ma piuttosto incorporare la nostra comprensione delle sue esigenze e del suo comportamento nel processo di concentrazione sul concetto generale e pratico di nullability.

Fai attenzione, quando boxi un tipo nullable ( Nullable 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 si perde il fatto che era nullable.

La soluzione più semplice che ho trovato è quella di implementare la soluzione di Microsoft ( Come: Identificare un tipo Nullable (C # Programming Guide) ) come metodo di estensione:

 public static bool IsNullable(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() != typeof(Nullable<>); } 

Questo può quindi essere chiamato così:

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

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

Forse un po ‘fuori tema, ma ancora alcune informazioni interessanti. Trovo molte persone che usano Nullable.GetUnderlyingType() != null per id quadro se un tipo è annullabile. Questo ovviamente funziona, ma Microsoft consiglia il seguente type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) (vedere http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

Ho guardato questo dal punto di vista delle prestazioni. La conclusione del test (un milione di tentativi) sotto è che quando un tipo è un valore nullo, l’opzione Microsoft offre le migliori prestazioni.

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

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

So che stiamo parlando di una piccola quantità di tempo, ma tutti amano modificare i millisecondi :-)! Quindi se il tuo capo vuole che tu riduca alcuni millisecondi, allora questo è il tuo salvatore …

 /// Method for testing the performance of several options to determine if a type is nullable [TestMethod] public void IdentityNullablePerformanceTest() { int attempts = 1000000; Type nullableType = typeof(Nullable); 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(); } 

Questa versione:

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

:

 public static class IsNullable { 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.Result; 

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 T e Nullable e utilizzare il fatto che il metodo di estensione statica, corrispondente al tipo sottostante, verrà invocato e ha la precedenza sul metodo di estensione T generico.

Per T :

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

e per Nullable

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

L’uso di Reflection e type.IsGenericType … non ha funzionato sul mio attuale set di .NET Runtime. Né ha aiutato la documentazione MSDN .

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

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

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 sono passati

  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 reali

  [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); 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); var result = typ.IsNullable(); Assert.IsFalse(result); }