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.