Wie überprüfe ich, ob ein Objekt nullwertfähig ist?


202

Wie überprüfe ich, ob ein bestimmtes Objekt nullbar ist? Mit anderen Worten, wie implementiere ich die folgende Methode ...

bool IsNullableValueType(object o)
{
    ...
}

EDIT: Ich suche nach nullbaren Werttypen. Ich hatte keine Ref-Typen im Sinn.

//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 bezieht sich jetzt auf ein Objekt vom Typ bool( System.Boolean) mit dem Wert gleich true. Was ich wirklich wollte, war ein Objekt vom TypNullable<bool>

Um dies zu umgehen, habe ich mich entschlossen, zu überprüfen, ob o nullbar ist, und einen nullbaren Wrapper um obj zu erstellen.


Sollte der Code Zeichenfolgen als nullbar enthalten? Sie sind ein nicht generischer ValueType, der scheinbar nullwertfähig ist. Oder sind sie kein ValueType?
TamusJRoyce

String ist kein ValueType. Es ist ein Referenztyp.
Suncat2000

Das ist eine wirklich gute Frage! Der 'Type.IsNullableType ()' täuscht, weil er tatsächlich nur prüft, ob der Typ ein 'Nullable <T>' ist, der keine erwarteten Ergebnisse zurückgibt, wenn Sie tatsächlich nach Typen suchen möchten, die eine Null akzeptieren können value (z. B. habe ich versucht, mit a.IsNullableType () zu verwenden, wobei 'a' ein zur Laufzeit festgelegter 'typeof (string)' war)
ErrCode

Die Antwort ist in fieldInfo.FieldType: Überprüfen Sie, ob FieldType generisch und der generische Typ vom Typ Nullable <> ist. (Beispiel: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). Versuchen Sie nicht, obj.GetType () abzurufen. Es wird UndelyingSystemType mit der Variablen T <Null> (in Ihrem Fall vom Typ Boolean anstelle von <Boolean> Nullable) angezeigt. Dies ist ein Boxproblem.
SoLaR

Antworten:


271

Es gibt zwei Arten von Nullwerten - Nullable<T>und Referenztypen.

Jon hat mich korrigiert, dass es schwierig ist, einen Typ zu bekommen, wenn er verpackt ist, aber Sie können mit Generika: - Wie wäre es unten? Dies ist eigentlich ein Testtyp T, aber die Verwendung des objParameters dient lediglich der generischen Typinferenz (um das Aufrufen zu vereinfachen) - ohne den objParameter würde er jedoch fast identisch funktionieren.

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
}

Dies funktioniert jedoch nicht so gut, wenn Sie den Wert bereits in eine Objektvariable eingefügt haben.

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


7
Die letzte Zeile ist nur gültig, wenn Sie es irgendwie schaffen, eine Nullable <T> in einer Box zu erhalten, anstatt direkt nach T zu boxen. Es ist möglich, aber schwierig, das zu erreichen, woran ich mich erinnere.
Jon Skeet

Dieser Code war für mich hilfreich, nicht weil ich eine Nullable <T> in einer Box erhalten habe, sondern weil ich eine generische WPF-Konverter-Basisklasse geschrieben habe und einige Eigenschaften nullbar sind. Daher habe ich Nullable.GetUnderlyingType verwendet, um diesen Fall zu erkennen, und Activator.CreateInstance, um ihn zu erstellen eine Boxed Nullable (Convert.ChangeType verarbeitet übrigens keine Nullables).
Qwertie

1
@Abel, wenn Sie meinen, dass er seine Bearbeitung vorgenommen hat, um zu verdeutlichen, dass er keine Referenztypen berücksichtigt hat, denke ich, dass meine Antwort vor dieser Bearbeitung lag. Ich vermute, dass der Leser dort seine eigene Entscheidung treffen kann, basierend auf seinen eigenen Bedürfnissen (bestätigt: sein Kommentar wurde um 14:42 Uhr neu geschrieben; meine Antwort war alle <= 14:34
Uhr

1
Wird (obj == null) eine Ausnahme auslösen, wenn obj = 1 ist?
Qi Fan

3
@JustinMorgan Wenn Tein generischer Parameter eingeschränkt durch T : struct, dann Twird sein nicht erlaubt Nullable<>, so dass Sie keine Prüfung in diesem Fall brauchen! Ich weiß, dass der Typ Nullable<>eine Struktur ist, aber in C # where T : structschließt die Einschränkung nullbare Werttypen ausdrücklich aus. In der Spezifikation heißt es: "Beachten Sie, dass ein nullbarer Typ (§4.1.10), obwohl er als Werttyp klassifiziert ist, die Einschränkung für den Werttyp nicht erfüllt."
Jeppe Stig Nielsen

46

Es gibt eine sehr einfache Lösung mit Methodenüberladungen

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

Auszug:

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

dann

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
plus eins für Sie, Sir, für das Hinzufügen von Testfällen. Ich habe diese Testfälle verwendet, um alle anderen Antworten zu überprüfen. Mehr Leute sollten dieses Extra gehen.
Marty Neal

4
Für das, was es wert ist, funktioniert dies nicht in VB.NET. Dies führt in allen Situationen, in denen zurückgegeben werden Truewürde, zu dem Compilerfehler " Überlastungsauflösung fehlgeschlagen, da kein zugängliches 'IsNullable' für diese Argumente am spezifischsten ist " .
ckittel

1
Ich mag diese Lösung wirklich - und es ist eine Schande, dass VB damit nicht umgehen kann. Ich habe versucht, mit ValueType herumzuarbeiten, hatte jedoch Probleme damit, dass der VB-Compiler inkonsistent war, welche Überlastung verwendet werden sollte, je nachdem, ob er als gemeinsam genutzte Methode oder als Erweiterung aufgerufen wurde. Ich habe sogar eine Frage dazu gestellt, da dies seltsam erscheint: stackoverflow.com/ Fragen / 12319591 /…
James Close

22
Sie überprüfen den Kompilierungszeittyp , aber es ist bereits offensichtlich (aus Intellisense), ob der Kompilierungszeittyp nullbar ist ( System.Nullable<>). Wenn Sie sagen object g = e;und dann ValueTypeHelper.IsNullable(g), was erwarten Sie zu erhalten?
Jeppe Stig Nielsen

18
Ich habe es gerade überprüft. Das funktioniert nicht , wie Jeppe sagte. Wenn die Variablen in ein Objekt umgewandelt werden, wird immer false zurückgegeben. Auf diese Weise können Sie den Typ eines unbekannten Objekts zur Laufzeit nicht bestimmen. Dies funktioniert nur, wenn der Typ zur Kompilierungszeit festgelegt ist und Sie in diesem Fall überhaupt keine Laufzeitprüfung benötigen.
HugoRune

30

Die Frage "Wie kann überprüft werden, ob ein Typ nullwertfähig ist?" ist eigentlich "Wie prüfe Nullable<>ich , ob ein Typ ist ?", was verallgemeinert werden kann auf "Wie prüfe ich, ob ein Typ ein konstruierter Typ eines generischen Typs ist?", so dass nicht nur die Frage "Ist Nullable<int>ein Nullable<>?" beantwortet wird. aber auch "Ist List<int>ein List<>?".

Die meisten der bereitgestellten Lösungen verwenden die Nullable.GetUnderlyingType()Methode, die offensichtlich nur mit dem Fall von funktioniert Nullable<>. Ich habe die allgemeine reflektierende Lösung, die mit jedem generischen Typ funktioniert, nicht gesehen, deshalb habe ich beschlossen, sie hier für die Nachwelt hinzuzufügen, obwohl diese Frage bereits vor langer Zeit beantwortet wurde.

Um zu überprüfen, ob ein Typ eine Form der Nullable<>Reflektion ist, müssen Sie zuerst Ihren konstruierten generischen Typ Nullable<int>in die generische Typdefinition konvertieren Nullable<>. Sie können dies mit der GetGenericTypeDefinition()Methode der TypeKlasse tun . Sie können den resultierenden Typ dann vergleichen mit Nullable<>:

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

Das gleiche kann auf jeden generischen Typ angewendet werden:

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

Mehrere Typen mögen gleich erscheinen, aber eine unterschiedliche Anzahl von Typargumenten bedeutet, dass es sich um einen völlig anderen Typ handelt.

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

Da TypeObjekte einmal pro Typ instanziiert werden, können Sie die Referenzgleichheit zwischen ihnen überprüfen. Wenn Sie also überprüfen möchten, ob zwei Objekte dieselbe generische Typdefinition haben, können Sie Folgendes schreiben:

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

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

Wenn Sie überprüfen möchten, ob ein Objekt nullbar ist und nicht a Type, können Sie die obige Technik zusammen mit der Lösung von Marc Gravell verwenden, um eine recht einfache Methode zu erstellen:

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

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

@ AllonGuralnek Meine Antwort enthält eine vereinfachte Version. Ich wollte es als Bearbeitung machen und da mein Ruf nicht Ihr Niveau ist, würde es ohne meinen Namen auf Ihrer Antwort bearbeitet werden, trotzdem scheint es, dass die Rezension mich immer ins Bein schießt, dass es den Autor adressiert, selbst wenn es so wäre nicht. Seltsame Welt, manche Leute bekommen keine Definitionen :).
Ipavlu

@ipavlu: Ihre Version ist nicht vereinfacht, es ist in der Tat komplizierter. Ich denke, Sie meinen, es ist optimiert, da Sie das Ergebnis zwischenspeichern. Das macht es schwieriger zu verstehen.
Allon Guralnek

@ AllonGuralnek statische generische Klasse und statische einmal initialisierte Felder, das ist kompliziert? Lieber Gott, ich habe ein schreckliches Verbrechen begangen :).
Ipavlu

@ipavku: Ja, weil es nichts mit der Frage "Wie prüfe ich, ob ein Objekt nullbar ist?" zu tun hat. Ich versuche, es einfach und auf den Punkt zu bringen, und ich vermeide es, unnötige und nicht verwandte Konzepte einzuführen.
Allon Guralnek

1
@nawfal: Wenn ich Sie richtig verstanden habe, suchen Sie meine Implementierung angesichts der Existenz der Nullable.GetUnderlyingType()bereits vom Framework bereitgestellten. Warum nicht einfach die Methode im Framework verwenden? Nun, du solltest. Es ist klarer, prägnanter und besser getestet. Aber in meinem Beitrag versuche ich zu lehren, wie man Reflexion verwendet, um die gewünschten Informationen zu erhalten, damit jemand sie auf jeden Typ anwenden kann (indem er sie typeof(Nullable<>)durch einen anderen Typ ersetzt). Wenn Sie sich die Quellen von GetUnderlyingType()(original oder dekompiliert) ansehen , werden Sie feststellen, dass sie meinem Code sehr ähnlich sind.
Allon Guralnek

30

Das funktioniert bei mir und scheint einfach:

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

Für Werttypen:

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

7
Für das, was es wert ist, ist dies auch der Test, der von Microsoft verwendet wird
canton7

1
Schön ... Ist das nicht die beste Antwort, weil es später kam? Ich finde die Top-Antwort so verwirrend.
Vincent Buscarello

1
Dies sollte die beste Antwort sein. Nachdem ich tagelang verschiedene Methoden ausprobiert hatte, dachte ich zufällig an diese Lösung, probierte sie aus und sie scheint perfekt zu funktionieren (verglichen mit der am besten bewerteten Antwort)
user3163495

2
Dies ist eine hervorragende Lösung, um herauszufinden, ob eine Instanz auf NULL gesetzt werden kann. Sie gibt jedoch true für alles zurück , was auf null gesetzt werden kann, einschließlich gewöhnlicher Objekte. Es ist wichtig zu wissen, dass die ursprüngliche Frage speziell Nullable ValueTypes erkennen wollte.
JamesHoux

20

Nun, Sie könnten verwenden:

return !(o is ValueType);

... aber ein Objekt selbst ist nicht nullbar oder anders - ein Typ ist. Wie wollten Sie das nutzen?


2
Das hat mich ein bisschen umgehauen. zB int? i = 5; typeof (i) gibt System.Int32 anstelle von Nullable <Int32> zurück - typeof (int?) gibt Nullable <Int32> zurück. Wo kann ich Klarheit zu diesem Thema bekommen?
Gishu

2
typeof (i) gibt einen Compilerfehler aus - Sie können typeof nicht mit einer Variablen verwenden. Was hast du eigentlich gemacht?
Jon Skeet

15
i.GetType () wird zuerst in Object boxen, und es gibt keinen nullbaren Typ mit Box - Nullable <int> wird in eine Nullreferenz oder ein int in Box gesetzt.
Jon Skeet

Dieser Weg ist besser als Nullable.GetUnderlyingType (Typ)! = Null?
Kiquenet

@Kiquenet: Wir haben keine haben den Typ hier - nur den Wert.
Jon Skeet

11

Der einfachste Weg, den ich herausfinden kann, ist:

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

+1. Hervorragende Lösung für Boxed-Null-fähige Typen. Ich habe dies noch nicht speziell getestet. Wenn also jemand anderes dies überprüfen kann, wäre er dankbar.
TamusJRoyce

Ich habe es bereits getestet. Ich musste eine Art NullableTyp erstellen, aber mit unterschiedlicher Semantik. In meiner Situation sollte ich nullals gültigen Wert unterstützen und auch überhaupt keinen Wert unterstützen. Also ein OptionalTyp erstellt. Da es notwendig war, nullWerte zu unterstützen , musste ich Nullableim Rahmen meiner Implementierung auch Code für den Umgang mit Werten implementieren. Von dort kam dieser Code.
CARLOS LOTH

9
Ich denke, diese Lösung ist falsch. Das Übergeben eines Nullable-Werttyps als Argument an eine Methode, die einen Parameter vom Typ object erwartet, sollte zum Boxen führen. Nullable ist ein Werttyp und das Ergebnis der Boxkonvertierung ist ein Referenztyp. Es gibt keine Nullen in Kästchen. Ich glaube, diese Methode gibt immer false zurück?
Mishax

1
Irgendein Test darüber wie eine andere Antwort?
Kiquenet

5
Es funktioniert nicht wegen des Boxwerts. Es wird immer FALSE zurückgegeben.
N Rocking

10

Hier gibt es zwei Probleme: 1) Testen, ob ein Typ nullwertfähig ist; und 2) Testen, um zu sehen, ob ein Objekt einen nullbaren Typ darstellt.

Für Problem 1 (Testen eines Typs) ist hier eine Lösung, die ich in meinen eigenen Systemen verwendet habe: TypeIsNullable-Check-Lösung

In Problem 2 (Testen eines Objekts) funktioniert die obige Lösung von Dean Chalk für Werttypen, jedoch nicht für Referenztypen, da die Verwendung der <T> -Überladung immer false zurückgibt. Da Referenztypen von Natur aus nullwertfähig sind, sollte das Testen eines Referenztyps immer true zurückgeben. Eine Erläuterung dieser Semantik finden Sie im Hinweis [Über "Nullbarkeit"] unten. Hier ist meine Modifikation von Deans Ansatz:

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

Und hier ist meine Änderung am Client-Testcode für die obige Lösung:

    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

Der Grund, warum ich Deans Ansatz in IsObjectNullable <T> (T t) geändert habe, ist, dass sein ursprünglicher Ansatz für einen Referenztyp immer false zurückgegeben hat. Da eine Methode wie IsObjectNullable in der Lage sein sollte, Referenztypwerte zu verarbeiten, und alle Referenztypen von Natur aus nullwertfähig sind, sollte die Methode immer true zurückgeben, wenn entweder ein Referenztyp oder eine Null übergeben wird.

Die beiden oben genannten Methoden könnten durch die folgende Einzelmethode ersetzt werden und dieselbe Ausgabe erzielen:

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

Das Problem bei diesem letzten Ansatz mit nur einer Methode besteht jedoch darin, dass die Leistung leidet, wenn ein Nullable <T> -Parameter verwendet wird. Das Ausführen der letzten Zeile dieser einzelnen Methode benötigt viel mehr Prozessorzeit als das Auswählen der zweiten Methodenüberladung, die zuvor angezeigt wurde, wenn im IsObjectNullable-Aufruf ein Parameter vom Typ Nullable <T> verwendet wird. Daher besteht die optimale Lösung darin, den hier dargestellten Zwei-Methoden-Ansatz zu verwenden.

CAVEAT: Diese Methode funktioniert nur dann zuverlässig, wenn sie unter Verwendung der ursprünglichen Objektreferenz oder einer exakten Kopie aufgerufen wird, wie in den Beispielen gezeigt. Wenn ein nullbares Objekt jedoch einem anderen Typ (z. B. einem Objekt usw.) zugeordnet ist, anstatt in seiner ursprünglichen nullbaren <> Form zu bleiben, funktioniert diese Methode nicht zuverlässig. Wenn der Code, der diese Methode aufruft, nicht die ursprüngliche Objektreferenz ohne Box oder eine exakte Kopie verwendet, kann er die Nullfähigkeit des Objekts mit dieser Methode nicht zuverlässig bestimmen.

In den meisten Codierungsszenarien muss man sich zur Bestimmung der Nullbarkeit stattdessen darauf verlassen, den Typ des ursprünglichen Objekts und nicht dessen Referenz zu testen (z. B. muss der Code Zugriff auf den ursprünglichen Typ des Objekts haben, um die Nullfähigkeit zu bestimmen). In diesen häufigeren Fällen ist IsTypeNullable (siehe Link) eine zuverlässige Methode zur Bestimmung der Nullfähigkeit.

PS - Über "Nullability"

Ich sollte eine Aussage über die Nichtigkeit, die ich in einem separaten Beitrag gemacht habe, wiederholen, die direkt für die richtige Behandlung dieses Themas gilt. Das heißt, ich glaube, der Fokus der Diskussion hier sollte nicht darauf liegen, zu überprüfen, ob ein Objekt ein generischer Nullable-Typ ist, sondern ob man einem Objekt seines Typs den Wert Null zuweisen kann. Mit anderen Worten, ich denke, wir sollten bestimmen, ob ein Objekttyp nullbar ist, nicht, ob er nullbar ist. Der Unterschied liegt in der Semantik, nämlich den praktischen Gründen für die Bestimmung der Nullfähigkeit, was normalerweise alles ist, was zählt.

In einem System, das Objekte mit möglicherweise bis zur Laufzeit unbekannten Typen verwendet (Webdienste, Fernaufrufe, Datenbanken, Feeds usw.), besteht eine häufige Anforderung darin, zu bestimmen, ob dem Objekt eine Null zugewiesen werden kann oder ob das Objekt möglicherweise enthält eine Null. Das Ausführen solcher Operationen mit nicht nullbaren Typen führt wahrscheinlich zu Fehlern, normalerweise Ausnahmen, die sowohl hinsichtlich der Leistung als auch der Codierungsanforderungen sehr teuer sind. Um den äußerst bevorzugten Ansatz zu verfolgen, solche Probleme proaktiv zu vermeiden, muss bestimmt werden, ob ein Objekt eines beliebigen Typs eine Null enthalten kann; dh, ob es im Allgemeinen "nullbar" ist.

In einem sehr praktischen und typischen Sinne bedeutet die Nullbarkeit in .NET-Begriffen keineswegs unbedingt, dass der Typ eines Objekts eine Form von Nullable ist. In vielen Fällen haben Objekte tatsächlich Referenztypen, können einen Nullwert enthalten und sind daher alle nullwertfähig. Keiner von diesen hat einen Nullable-Typ. Aus praktischen Gründen sollten daher in den meisten Szenarien Tests für das allgemeine Konzept der Nullbarkeit im Vergleich zum implementierungsabhängigen Konzept der Nullbarkeit durchgeführt werden. Wir sollten uns also nicht nur auf den .NET Nullable-Typ konzentrieren, sondern unser Verständnis seiner Anforderungen und seines Verhaltens in den Prozess der Fokussierung auf das allgemeine, praktische Konzept der Nullbarkeit einbeziehen.


8

Die einfachste Lösung, die ich gefunden habe, ist die Implementierung der Microsoft-Lösung ( Gewusst wie: Identifizieren eines nullbaren Typs (C # -Programmierhandbuch) ) als Erweiterungsmethode:

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

Dies kann dann so genannt werden:

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

Dies scheint auch eine logische Art des Zugriffs zu sein, IsNullable()da es zu allen anderen IsXxxx()Methoden der TypeKlasse passt .


1
Wollten Sie nicht "==" anstelle von "! =" Verwenden?
Vkelman

Guter Ort @vkelman Anstatt diese Änderung vorzunehmen, habe ich die Antwort aktualisiert, um den aktuellen Vorschlag von Microsoft zu verwenden, da sich dies geändert hat, seit ich dies geschrieben habe.
sclarke81

6

Seien Sie vorsichtig, wenn Sie einen nullbaren Typ ( Nullable<int>oder beispielsweise int?) Boxen :

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

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

Es wird zu einem echten Referenztyp, sodass Sie die Tatsache verlieren, dass es nullbar war.


3

Vielleicht ein bisschen abseits des Themas, aber immer noch einige interessante Informationen. Ich finde viele Leute, die Nullable.GetUnderlyingType() != nullsich identifizieren, wenn ein Typ nullbar ist. Dies funktioniert natürlich, aber Microsoft empfiehlt Folgendes type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(siehe http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

Ich habe das von einer Performance-Seite aus gesehen. Die Schlussfolgerung des folgenden Tests (eine Million Versuche) lautet, dass die Microsoft-Option die beste Leistung liefert, wenn ein Typ nullwertfähig ist.

Nullable.GetUnderlyingType (): 1335 ms (dreimal langsamer)

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

Ich weiß, dass wir über eine kleine Menge Zeit sprechen, aber jeder liebt es, die Millisekunden zu optimieren :-)! Wenn Ihr Chef möchte, dass Sie einige Millisekunden reduzieren, dann ist dies Ihr Retter ...

/// <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
Hallo, es gibt wahrscheinlich ein Problem mit der Zeitmessung. Der Assert kann die Ergebnisse beeinflussen. Haben Sie ohne Assert getestet? Auch Console.WriteLine sollte sich außerhalb des Messbereichs befinden. +1 für einen Versuch, Leistungsprobleme zu quantifizieren :).
Ipavlu

@ipavlu Console.WriteLineist in der Tat außerhalb gemessenen Bereich;)
Nawfal

Roel sollte, wie ipavlu erwähnt hat, Assertaußerhalb der Schleife sein. Zweitens sollten Sie es auch gegen Nicht-Nullables testen, um auf falsche Fälle zu testen. Ich habe einen ähnlichen Test durchgeführt (2 Nulables und 4 Nicht-Nullables) und ich bekomme ~ 2 Sekunden für GetUnderlyingTypeund ~ 1 Sekunde für GetGenericTypeDefinition, dh GetGenericTypeDefinitionist zweimal schneller (nicht dreimal).
Nawfal

Habe eine weitere Runde mit 2 Nullables und 2 Non-Nullables gemacht - diesmal GetUnderlyingTypewar es 2,5 mal langsamer. Mit nur nicht nullbaren Werten - diesmal sind beide Hals und Nacken.
Nawfal

Noch wichtiger GetUnderlyingTypeist jedoch, dass Sie die Nullbarkeit überprüfen und den zugrunde liegenden Typ abrufen müssen, wenn er nullbar ist. Dies ist sehr nützlich und Sie sehen Muster oft wie Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). Es ist wie ein asSchlüsselwort, prüft auf die Besetzung sowie das Ergebnis. Wenn Sie den zugrunde liegenden Typ von nullable zurückbekommen möchten, GetGenericTypeDefinitionist es eine schlechte Idee , eine Überprüfung durchzuführen und dann einen generischen Typ zu erhalten. Ist auch GetUnderlyingTypeviel lesbarer und unvergesslicher. Es würde mir nichts ausmachen, wenn ich es nur ~ 1000 Mal mache.
Nawfal

0

Diese Version:

  • Caching-Ergebnisse sind schneller,
  • erfordert keine unnötigen Variablen wie Methode (T obj)
  • NICHT KOMPLIZIERT :),
  • Nur statische generische Klasse, die einmal berechnete Felder hat

::

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;

Ich denke, Sie haben sich selbst mit dieser statischen Deklaration 'is_nullable' geantwortet. Tipp: Objekte mit int deklarieren? (Objekt a = (int?) 8;) und sehen, was passiert.
SoLaR

0

Folgendes habe ich mir ausgedacht, da alles andere - zumindest auf der SPS - Portable Class Library / .NET Core mit> = C # 6 zu versagen schien

Lösung: Erweitern Sie statische Methoden für jeden Typ Tund Nullable<T>verwenden Sie die Tatsache, dass die statische Erweiterungsmethode, die dem zugrunde liegenden Typ entspricht, aufgerufen wird und Vorrang vor der generischen TErweiterungsmethode hat.

Für T:

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

und für Nullable<T>

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

Die Verwendung von Reflection und type.IsGenericType... funktionierte bei meinem aktuellen Satz von .NET-Laufzeiten nicht. Die MSDN-Dokumentation hat auch nicht geholfen.

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

Zum Teil, weil die Reflection-API in .NET Core erheblich geändert wurde.


0

Ich denke, diejenigen, die die von Microsoft vorgeschlagenen Tests verwenden, IsGenericTypesind gut, aber im Code für GetUnderlyingTypeverwendet Microsoft einen zusätzlichen Test, um sicherzustellen, dass Sie die generische Typdefinition nicht bestanden haben Nullable<>:

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

-1

ein einfacher Weg, dies zu tun:

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

        return true;
    }

Dies sind meine Unit-Tests und alle bestanden

    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

tatsächliche Unit-Tests

    [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);
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.