Как я могу преобразовать System.Type в его обнуляемую версию? - PullRequest
71 голосов
/ 20 сентября 2008

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

Таким образом, легко получить базовый тип из типа NULL, но как мне получить версию NNET для NULL?

Итак, у меня есть

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

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

Ответы [ 4 ]

102 голосов
/ 20 сентября 2008

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

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;
}
14 голосов
/ 13 октября 2011

У меня есть пара методов, которые я написал в своей служебной библиотеке, на которые я сильно полагался. Первый - это метод, который преобразует любой тип в соответствующую ему Nullable форму:

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

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

Mark

P.S. - про "обнуляемость"

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

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

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

9 голосов
/ 19 марта 2010

Ответ Лаймана великолепен и помог мне, однако есть еще одна ошибка, которую нужно исправить.

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

(извините, но я не мог просто оставить комментарий к ответу Лаймана, потому что я был новичком и у меня еще не было достаточно представителей.)

2 голосов
/ 20 сентября 2008

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

Редактировать: Как следует из комментариев, на самом деле Nullable<> специально обрабатывается , а во время выполнения загружается, как описано в этой статье .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...