Как преобразовать System.Type в свою нулевую версию?

Еще один из них: "Есть ли более простой встроенный способ делать что-то вместо моего вспомогательного метода?"

Так легко получить базовый тип от типа NULL, но как получить нулевую версию .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

Есть ли встроенный метод?

Ответ 1

Вот код, который я использую:

Type GetNullableType(Type type) {
    // Use Nullable.GetUnderlyingType() to remove the Nullable<T> wrapper if type is already nullable.
    type = Nullable.GetUnderlyingType(type);
    if (type.IsValueType)
        return typeof(Nullable<>).MakeGenericType(type);
    else
        return type;
}

Изменить: у исходного кода была ошибка, в которой он будет вести себя неожиданно, если type сам был Nullable<T>.

Ответ 2

У меня есть несколько методов, которые я написал в своей библиотеке, на которых я сильно полагался. Первый - это метод, который преобразует любой тип в соответствующий 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;
    }

Второй метод просто сообщает, является ли заданный тип нулевым. Этот метод вызывается первым и полезен отдельно:

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

Наслаждайтесь!

Марк

P.S. - Об "недействительности"

Я должен повторить высказывание о nullability, которое я сделал в отдельном сообщении, которое применяется непосредственно для правильного решения этой темы. То есть, я считаю, что основное внимание в обсуждении здесь не должно быть, как проверить, является ли объект общим типом Nullable, а скорее можно ли присвоить значение null объекту его типа. Другими словами, я думаю, мы должны определить, является ли тип объекта нулевым, а не является ли он Nullable. Разница заключается в семантике, а именно в практических причинах определения неопределенности, что обычно имеет значение.

В системе, использующей объекты с типами, которые неизвестны до времени выполнения (веб-службы, удаленные вызовы, базы данных, каналы и т.д.), общее требование состоит в том, чтобы определить, можно ли присвоить нуль объекту или объект может содержать нуль. Выполнение таких операций с непустыми типами может привести к ошибкам, обычно исключениям, которые очень дороги как с точки зрения производительности, так и с требованиями кодирования. Чтобы принять крайне предпочтительный подход к проактивному устранению таких проблем, необходимо определить, способен ли объект произвольного типа содержать нуль; то есть, является ли оно вообще "нулевым".

В очень практичном и типичном смысле неопределенность в .NET-терминах вовсе не обязательно означает, что объект Type является формой Nullable. Во многих случаях на самом деле объекты имеют ссылочные типы, могут содержать нулевое значение и, следовательно, все они могут быть обнуляемы; ни один из них не имеет типа Nullable. Поэтому для практических целей в большинстве сценариев тестирование должно проводиться для общей концепции обнуляемости по сравнению с концепцией Nullable, зависящей от реализации. Поэтому мы не должны зависеть, сосредотачиваясь исключительно на типе .NET Nullable, но скорее включаем наше понимание его требований и поведения в процессе фокусировки на общей практической концепции обнуления.

Ответ 3

Ответ на Lyman велик и помог мне, однако, там еще одна ошибка, которая должна быть исправлена.

Nullable.GetUnderlyingType(type) следует вызывать только в том случае, если тип уже не является типом Nullable. В противном случае, кажется, ошибочно возвращается значение null, когда тип происходит от System.RuntimeType (например, когда я проходил в typeof(System.Int32)). В приведенной ниже версии избегайте вызова 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, потому что я был новым и еще не имел достаточного количества репутации.)

Ответ 4

Нет ничего встроенного, о котором я знаю, поскольку int? и т.д. - это просто синтаксический сахар для Nullable<T>; и ему не уделяется особого внимания. Это особенно маловероятно, если вы пытаетесь получить это от информации типа данного типа. Как правило, это всегда требует некоторого "сворачивания вашего" кода в качестве заданного. Вам нужно будет использовать Reflection для создания нового типа Nullable с параметром типа входного типа.

Изменить:. В качестве комментариев на самом деле Nullable<> обрабатывается специально, а во время выполнения загрузки, как описано в this статья.