How do I convert a System.Type to its nullable version?

前端 未结 4 1392
长发绾君心
长发绾君心 2021-01-30 19:21

Once again one of those: \"Is there an easier built-in way of doing things instead of my helper method?\"

So it\'s easy to get the underlying type from a nullable type,

相关标签:
4条回答
  • 2021-01-30 19:59

    Lyman's answer is great and has helped me, however, there's one more bug which needs to be fixed.

    Nullable.GetUnderlyingType(type) should only be called iff the type isn't already a Nullable type. Otherwise, it seems to erroneously return null when the type derives from System.RuntimeType (such as when I pass in typeof(System.Int32) ). The below version avoids needing to call Nullable.GetUnderlyingType(type) by checking if the type is Nullable instead.

    Below you'll find an ExtensionMethod version of this method which will immediately return the type unless it's a ValueType that's not already 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);
    }
    

    (I'm sorry, but I couldn't simply post a comment to Lyman's answer because I was new and didn't have enough rep yet.)

    0 讨论(0)
  • 2021-01-30 20:02

    There isn't anything built in that I know of, as the int?, etc. is just syntactic sugar for Nullable<T>; and isn't given special treatment beyond that. It's especially unlikely given you're attempting to obtain this from the type information of a given type. Typically that always necessitates some 'roll your own' code as a given. You would have to use Reflection to create a new Nullable type with type parameter of the input type.

    Edit: As the comments suggest actually Nullable<> is treated specially, and in the runtime to boot as explained in this article.

    0 讨论(0)
  • 2021-01-30 20:13

    I have a couple of methods I've written in my utility library that I've heavily relied on. The first is a method that converts any Type to its corresponding 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;
        }
    

    The second method simply reports whether a given Type is nullable. This method is called by the first and is useful separately:

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

    The above IsTypeNullable implementation works like a champ every time, but it's slightly verbose and slow in its last code line. The following code body is the same as above for IsTypeNullable, except the last code line is simpler and faster:

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

    Enjoy!

    Mark

    P.S. - About "nullability"

    I should repeat a statement about nullability I made in a separate post, which applies directly to properly addressing this topic. That is, I believe the focus of the discussion here should not be how to check to see if an object is a generic Nullable type, but rather whether one can assign a value of null to an object of its type. In other words, I think we should be determining whether an object type is nullable, not whether it is Nullable. The difference is in semantics, namely the practical reasons for determining nullability, which is usually all that matters.

    In a system using objects with types possibly unknown until run-time (web services, remote calls, databases, feeds, etc.), a common requirement is to determine whether a null can be assigned to the object, or whether the object might contain a null. Performing such operations on non-nullable types will likely produce errors, usually exceptions, which are very expensive both in terms of performance and coding requirements. To take the highly-preferred approach of proactively avoiding such problems, it is necessary to determine whether an object of an arbitrary Type is capable of containing a null; i.e., whether it is generally 'nullable'.

    In a very practical and typical sense, nullability in .NET terms does not at all necessarily imply that an object's Type is a form of Nullable. In many cases in fact, objects have reference types, can contain a null value, and thus are all nullable; none of these have a Nullable type. Therefore, for practical purposes in most scenarios, testing should be done for the general concept of nullability, vs. the implementation-dependent concept of Nullable. So we should not be hung up by focusing solely on the .NET Nullable type but rather incorporate our understanding of its requirements and behavior in the process of focusing on the general, practical concept of nullability.

    0 讨论(0)
  • 2021-01-30 20:19

    Here is the code I use:

    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;
    }
    
    0 讨论(0)
提交回复
热议问题