Wie konvertiere ich einen System.Type in seine nullfähige Version?

79

Noch einmal eine davon: "Gibt es eine einfachere Möglichkeit, Dinge anstelle meiner Hilfsmethode zu erledigen?"

Es ist also einfach, den zugrunde liegenden Typ von einem nullbaren Typ abzurufen, aber wie erhalte ich die nullfähige Version eines .NET-Typs?

Also habe ich

typeof(int)
typeof(DateTime)
System.Type t = something;

und ich will

int? 
DateTime?

oder

Nullable<int> (which is the same)
if (t is primitive) then Nullable<T> else just T

Gibt es eine eingebaute Methode?

Alex Duggleby
quelle

Antworten:

114

Hier ist der Code, den ich benutze:

Type GetNullableType(Type type) {
    // Use Nullable.GetUnderlyingType() to remove the Nullable<T> wrapper if type is already nullable.
    type = Nullable.GetUnderlyingType(type) ?? type; // avoid type becoming null
    if (type.IsValueType)
        return typeof(Nullable<>).MakeGenericType(type);
    else
        return type;
}
Alex Lyman
quelle
Nett! Dies ist eine wirklich nette Lösung.
ljs
Gute Antwort! Genau das, wonach ich gesucht habe.
Alex Duggleby
10
Beachten Sie, dass Nullable.GetUnderlyingType null zurückgibt, wenn der Typ nicht nullbar ist. Daher müssen Sie dies überprüfen.
Amit G
1
@AmitG Du hast recht. Beim Übergeben des nullWerttyps an diese Funktion tritt ein Fehler auf.
Illuminator
1
Sie müssen wirklich sicherstellen, dass dies typenicht null ist, bevor Sie auf die IsValueTypeEigenschaft zugreifen , wie @AmitG sagte. -1 bis das behoben ist
Matt Thomas
16

Ich habe einige Methoden in meiner Utility-Bibliothek geschrieben, auf die ich mich stark verlassen habe. Die erste Methode konvertiert einen beliebigen Typ in die entsprechende Nullable <Type> -Form:

    /// <summary>
    /// [ <c>public static Type GetNullableType(Type TypeToConvert)</c> ]
    /// <para></para>
    /// Convert any Type to its Nullable&lt;T&gt; form, if possible
    /// </summary>
    /// <param name="TypeToConvert">The Type to convert</param>
    /// <returns>
    /// The Nullable&lt;T&gt; converted from the original type, the original type if it was already nullable, or null 
    /// if either <paramref name="TypeToConvert"/> could not be converted or if it was null.
    /// </returns>
    /// <remarks>
    /// To qualify to be converted to a nullable form, <paramref name="TypeToConvert"/> must contain a non-nullable value 
    /// type other than System.Void.  Otherwise, this method will return a null.
    /// </remarks>
    /// <seealso cref="Nullable&lt;T&gt;"/>
    public static Type GetNullableType(Type TypeToConvert)
    {
        // Abort if no type supplied
        if (TypeToConvert == null)
            return null;

        // If the given type is already nullable, just return it
        if (IsTypeNullable(TypeToConvert))
            return TypeToConvert;

        // If the type is a ValueType and is not System.Void, convert it to a Nullable<Type>
        if (TypeToConvert.IsValueType && TypeToConvert != typeof(void))
            return typeof(Nullable<>).MakeGenericType(TypeToConvert);

        // Done - no conversion
        return null;
    }

Die zweite Methode gibt einfach an, ob ein bestimmter Typ nullwertfähig ist. Diese Methode wird von der ersten aufgerufen und ist separat nützlich:

    /// <summary>
    /// [ <c>public static bool IsTypeNullable(Type TypeToTest)</c> ]
    /// <para></para>
    /// Reports whether a given Type is nullable (Nullable&lt; Type &gt;)
    /// </summary>
    /// <param name="TypeToTest">The Type to test</param>
    /// <returns>
    /// true = The given Type is a Nullable&lt; Type &gt;; false = The type is not nullable, or <paramref name="TypeToTest"/> 
    /// is null.
    /// </returns>
    /// <remarks>
    /// This method tests <paramref name="TypeToTest"/> and reports whether it is nullable (i.e. whether it is either a 
    /// reference type or a form of the generic Nullable&lt; T &gt; type).
    /// </remarks>
    /// <seealso cref="GetNullableType"/>
    public static bool IsTypeNullable(Type TypeToTest)
    {
        // Abort if no type supplied
        if (TypeToTest == null)
            return false;

        // If this is not a value type, it is a reference type, so it is automatically nullable
        //  (NOTE: All forms of Nullable<T> are value types)
        if (!TypeToTest.IsValueType)
            return true;

        // Report whether TypeToTest is a form of the Nullable<> type
        return TypeToTest.IsGenericType && TypeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

Die obige IsTypeNullable-Implementierung funktioniert jedes Mal wie ein Champion, ist jedoch in der letzten Codezeile etwas ausführlich und langsam. Der folgende Codekörper ist der gleiche wie oben für IsTypeNullable, außer dass die letzte Codezeile einfacher und schneller ist:

        // Abort if no type supplied
        if (TypeToTest == null)
            return false;

        // If this is not a value type, it is a reference type, so it is automatically nullable
        //  (NOTE: All forms of Nullable<T> are value types)
        if (!TypeToTest.IsValueType)
            return true;

        // Report whether an underlying Type exists (if it does, TypeToTest is a nullable Type)
        return Nullable.GetUnderlyingType(TypeToTest) != null;

Genießen!

Kennzeichen

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.

Mark Jones
quelle
9

Lymans Antwort ist großartig und hat mir geholfen, aber es gibt noch einen Fehler, der behoben werden muss.

Nullable.GetUnderlyingType(type)sollte nur aufgerufen werden, wenn der Typ noch kein NullableTyp ist. Andernfalls scheint es fälschlicherweise null zurückzugeben, wenn der Typ abgeleitet ist System.RuntimeType(z. B. wenn ich übergebe typeof(System.Int32)). Die folgende Version vermeidet das Aufrufen, Nullable.GetUnderlyingType(type)indem überprüft wird, ob der Typ Nullablestattdessen ist.

Unten finden Sie eine ExtensionMethodVersion dieser Methode, die den Typ sofort zurückgibt, sofern dies ValueTypenicht bereits geschehen ist Nullable.

Type NullableVersion(this Type sourceType)
{
    if(sourceType == null)
    {
        // Throw System.ArgumentNullException or return null, your preference
    }
    else if(sourceType == typeof(void))
    { // Special Handling - known cases where Exceptions would be thrown
        return null; // There is no Nullable version of void
    }

    return !sourceType.IsValueType
            || (sourceType.IsGenericType
               && sourceType.GetGenericTypeDefinition() == typeof(Nullable<>) )
        ? sourceType
        : typeof(Nullable<>).MakeGenericType(sourceType);
}

(Es tut mir leid, aber ich konnte nicht einfach einen Kommentar zu Lymans Antwort schreiben, weil ich neu war und noch nicht genug Repräsentanten hatte.)

Thracx
quelle
2

Es ist nichts eingebaut, von dem ich weiß, da das int?usw. nur syntaktischer Zucker ist Nullable<T>; und wird darüber hinaus nicht besonders behandelt. Es ist besonders unwahrscheinlich, dass Sie versuchen, dies aus den Typinformationen eines bestimmten Typs zu erhalten. In der Regel erfordert dies immer einen bestimmten Code. Sie müssten Reflection verwenden, um einen neuen NullableTyp mit dem Typparameter des Eingabetyps zu erstellen .

Edit: Da die Kommentare tatsächlich empfehlen Nullable<> sind speziell behandelt, und in der Runtime - Boot , wie unter diesem Artikel .

ljs
quelle
Eigentlich bin ich mir ziemlich sicher, dass die CLR eine besondere Magie hat, um Nullable <> etwas anders zu handhaben. Ich muss das überprüfen.
TraumaPony
Das würde mich interessieren, ich würde gerne zugeben, dass ich falsch
liege,
Da Sie zwei int?über den +Operator hinzufügen können , wissen wir , dass Nullables eine Sonderbehandlung erhalten, da diese Art der generischen Überladung des Operators sonst nicht funktionieren würde.
Konrad Rudolph