如何将System.Type转换为其可为空的版本?


79

再说一遍:“是否有一种比我的助手方法更简单的内置处理方式?”

因此从可为空的类型中获取基础类型很容易,但是如何获取.NET类型的可为空的版本呢?

所以我有

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

而且我要

int? 
DateTime?

要么

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

有内置方法吗?

Answers:


114

这是我使用的代码:

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

真好!这是一个非常整洁的解决方案。
ljs

好答案!正是我想要的。
Alex Duggleby

10
请注意,如果类型不可为null,则Nullable.GetUnderlyingType返回null,因此需要进行检查。
阿米特G

1
@AmitG你是对的。null将值类型传递给此函数时,我会遇到错误。
照明者

1
正如@AmitG所说,您确实需要type在访问IsValueType属性之前确保它不为null 。-1直到那是固定不变的
马特·托马斯

16

我已经在实用程序库中编写了一些非常依赖的方法。第一种是将任何Type转换为其对应的Nullable <Type>形式的方法:

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

第二种方法只是报告给定的Type是否可为空。此方法由第一个调用,并且分别有用:

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

上面的IsTypeNullable实现每次都像冠军一样工作,但是在最后一行代码中有些冗长和缓慢。以下代码主体与上面的IsTypeNullable相同,但最后一行代码更简单,更快捷:

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

请享用!

标记

PS-关于“可空性”

我应该在另一篇文章中重复有关可空性的声明,该声明直接适用于正确解决此主题。也就是说,我认为这里讨论的重点不应是如何检查对象是否为通用Nullable类型,而是应该是否可以为该类型的对象分配null值。换句话说,我认为我们应该确定对象类型是否可为空,而不是它是否可为空。区别在于语义上,即确定可为空性的实际原因,通常这很重要。

在使用对象的类型直到运行时可能未知的系统(Web服务,远程调用,数据库,提要等)中,一个共同的要求是确定是否可以为该对象分配空值,或者该对象是否可能包含空值。对非空类型执行此类操作可能会产生错误,通常是异常,这在性能和编码要求方面都非常昂贵。为了采取主动避免此类问题的首选方法,有必要确定任意类型的对象是否能够包含null。即,它通常是否为“可为空”。

在非常实际和典型的意义上,.NET术语中的可为空性完全不一定意味着对象的类型是可为空的形式。实际上,在许多情况下,对象具有引用类型,可以包含空值,因此都可以为空。这些都没有Nullable类型。因此,出于实用目的,在大多数情况下,应针对可为空性的一般概念进行测试,而不是依赖于实现的可为性概念。因此,我们不应该只专注于.NET Nullable类型,而应该将我们对它的要求和行为的理解纳入到专注于一般的,实用的nullability概念的过程中。


9

Lyman的回答很好,对我有所帮助,但是,还有一个错误需要修复。

Nullable.GetUnderlyingType(type)仅当类型还不是Nullable类型时才应调用它。否则,当类型派生时System.RuntimeType(例如当我传递时typeof(System.Int32)),它似乎错误地返回null 。下面的版本避免了Nullable.GetUnderlyingType(type)通过检查类型是否调用来进行调用Nullable

下面你会发现的ExtensionMethod这种方法,它会立即返回类型的版本,除非它是一个ValueType,这不是已经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);
}

(对不起,但是我不能简单地在Lyman的答案中发表评论,因为我是新来的,并且没有足够的代表。)


2

我不知道其中有什么内置内容,因为int?等仅是...的语法糖Nullable<T>。并且没有得到特别的待遇。鉴于您尝试从给定类型的类型信息中获取此信息,这种情况尤其不可能发生。通常,这总是需要一定的“滚动自己的”代码。您将必须使用Reflection创建Nullable带有输入类型的type参数的新类型。

编辑:正如评论所建议的,实际上Nullable<> 被特别对待,并且在运行时如本文所述进行启动。


实际上,我很确定CLR具有一些特殊的魔术来处理Nullable <>有所不同。我需要检查一下。
TraumaPony

我对此很感兴趣,很高兴承认我是错的:-)
ljs

既然你可以添加两个int?通过+运营商,我们知道Nullable小号获得特殊待遇,因为这种通用的操作符重载本来没有工作。
Konrad Rudolph
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.