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.
Antworten:
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 desobj
Parameters dient lediglich der generischen Typinferenz (um das Aufrufen zu vereinfachen) - ohne denobj
Parameter würde er jedoch fast identisch funktionieren.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
quelle
T
ein generischer Parameter eingeschränkt durchT : struct
, dannT
wird sein nicht erlaubtNullable<>
, so dass Sie keine Prüfung in diesem Fall brauchen! Ich weiß, dass der TypNullable<>
eine Struktur ist, aber in C #where T : struct
schließ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."Es gibt eine sehr einfache Lösung mit Methodenüberladungen
http://deanchalk.com/is-it-nullable/
Auszug:
dann
quelle
True
würde, zu dem Compilerfehler " Überlastungsauflösung fehlgeschlagen, da kein zugängliches 'IsNullable' für diese Argumente am spezifischsten ist " .System.Nullable<>
). Wenn Sie sagenobject g = e;
und dannValueTypeHelper.IsNullable(g)
, was erwarten Sie zu erhalten?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 "IstNullable<int>
einNullable<>
?" beantwortet wird. aber auch "IstList<int>
einList<>
?".Die meisten der bereitgestellten Lösungen verwenden die
Nullable.GetUnderlyingType()
Methode, die offensichtlich nur mit dem Fall von funktioniertNullable<>
. 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 TypNullable<int>
in die generische Typdefinition konvertierenNullable<>
. Sie können dies mit derGetGenericTypeDefinition()
Methode derType
Klasse tun . Sie können den resultierenden Typ dann vergleichen mitNullable<>
:Das gleiche kann auf jeden generischen Typ angewendet werden:
Mehrere Typen mögen gleich erscheinen, aber eine unterschiedliche Anzahl von Typargumenten bedeutet, dass es sich um einen völlig anderen Typ handelt.
Da
Type
Objekte 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: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:quelle
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 sietypeof(Nullable<>)
durch einen anderen Typ ersetzt). Wenn Sie sich die Quellen vonGetUnderlyingType()
(original oder dekompiliert) ansehen , werden Sie feststellen, dass sie meinem Code sehr ähnlich sind.Das funktioniert bei mir und scheint einfach:
Für Werttypen:
quelle
Nun, Sie könnten verwenden:
... aber ein Objekt selbst ist nicht nullbar oder anders - ein Typ ist. Wie wollten Sie das nutzen?
quelle
Der einfachste Weg, den ich herausfinden kann, ist:
quelle
Nullable
Typ erstellen, aber mit unterschiedlicher Semantik. In meiner Situation sollte ichnull
als gültigen Wert unterstützen und auch überhaupt keinen Wert unterstützen. Also einOptional
Typ erstellt. Da es notwendig war,null
Werte zu unterstützen , musste ichNullable
im Rahmen meiner Implementierung auch Code für den Umgang mit Werten implementieren. Von dort kam dieser Code.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:
Und hier ist meine Änderung am Client-Testcode für die obige Lösung:
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:
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.
quelle
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:
Dies kann dann so genannt werden:
Dies scheint auch eine logische Art des Zugriffs zu sein,
IsNullable()
da es zu allen anderenIsXxxx()
Methoden derType
Klasse passt .quelle
Seien Sie vorsichtig, wenn Sie einen nullbaren Typ (
Nullable<int>
oder beispielsweise int?) Boxen :Es wird zu einem echten Referenztyp, sodass Sie die Tatsache verlieren, dass es nullbar war.
quelle
Vielleicht ein bisschen abseits des Themas, aber immer noch einige interessante Informationen. Ich finde viele Leute, die
Nullable.GetUnderlyingType() != null
sich identifizieren, wenn ein Typ nullbar ist. Dies funktioniert natürlich, aber Microsoft empfiehlt Folgendestype.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 ...
quelle
Console.WriteLine
ist in der Tat außerhalb gemessenen Bereich;)Assert
auß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ürGetUnderlyingType
und ~ 1 Sekunde fürGetGenericTypeDefinition
, dhGetGenericTypeDefinition
ist zweimal schneller (nicht dreimal).GetUnderlyingType
war es 2,5 mal langsamer. Mit nur nicht nullbaren Werten - diesmal sind beide Hals und Nacken.GetUnderlyingType
ist 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 wieActivator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)
. Es ist wie einas
Schlüsselwort, prüft auf die Besetzung sowie das Ergebnis. Wenn Sie den zugrunde liegenden Typ von nullable zurückbekommen möchten,GetGenericTypeDefinition
ist es eine schlechte Idee , eine Überprüfung durchzuführen und dann einen generischen Typ zu erhalten. Ist auchGetUnderlyingType
viel lesbarer und unvergesslicher. Es würde mir nichts ausmachen, wenn ich es nur ~ 1000 Mal mache.Diese Version:
::
quelle
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
T
undNullable<T>
verwenden Sie die Tatsache, dass die statische Erweiterungsmethode, die dem zugrunde liegenden Typ entspricht, aufgerufen wird und Vorrang vor der generischenT
Erweiterungsmethode hat.Für
T
:und für
Nullable<T>
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.
quelle
Ich denke, diejenigen, die die von Microsoft vorgeschlagenen Tests verwenden,
IsGenericType
sind gut, aber im Code fürGetUnderlyingType
verwendet Microsoft einen zusätzlichen Test, um sicherzustellen, dass Sie die generische Typdefinition nicht bestanden habenNullable<>
:quelle
ein einfacher Weg, dies zu tun:
Dies sind meine Unit-Tests und alle bestanden
tatsächliche Unit-Tests
quelle