Як перетворити 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

Чи є вбудований метод?

Відповіді:


115

Ось код, який я використовую:

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
Зверніть увагу, що Nullable.GetUnderlyingType повертає null, якщо тип не має значення NULL, тому вам потрібно буде перевірити це.
Amit G

1
@AmitG Ви маєте рацію. Я переживаю nullпомилку, коли передаю тип значення у цю функцію.
Illuminator

1
Вам дійсно потрібно переконатися, що typeвоно не є нульовим, перш ніж отримати доступ до IsValueTypeвласності, як сказав @AmitG. -1, поки це не буде виправлено
Метт Томас

16

У моїй бібліотеці є кілька методів, на які я дуже покладався. Перший - це метод, який перетворює будь-який тип у відповідну форму 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;

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

Марка

PS - Про "недійсність"

Я повинен повторити заяву про недопустимість, яку я зробив в окремому дописі, який стосується безпосередньо правильного звернення до цієї теми. Тобто, я вважаю, що в центрі обговорення тут не повинно бути те, як перевірити, чи є об’єкт загальним типом Nullable, а чи можна присвоїти значення null об’єкту свого типу. Іншими словами, я вважаю, що ми повинні визначати, чи тип об'єкта має значення NULL, а не NULLABLE. Різниця полягає в семантиці, а саме в практичних причинах визначення визнання недійсним, що, як правило, має значення.

У системі, що використовує об’єкти з типами, які, можливо, невідомі до часу виконання (веб-служби, віддалені дзвінки, бази даних, стрічки тощо), загальною вимогою є визначення, чи може об’єкту бути присвоєне значення null, чи об’єкт може містити нуль. Виконання таких операцій з ненульованими типами, ймовірно, призведе до помилок, як правило, винятків, які є дуже дорогими як з точки зору продуктивності, так і вимог до кодування. Щоб скористатися вкрай бажаним підходом щодо попереджувального уникнення таких проблем, необхідно визначити, чи здатний об’єкт довільного типу містити нуль; тобто, чи є воно загалом "дозвільним".

У дуже практичному і типовому сенсі, можливість дозволеності в термінах .NET зовсім не означає, що Тип об'єкта є формою Nullable. Насправді в багатьох випадках об’єкти мають посилальні типи, можуть містити нульове значення і, отже, усі мають нульовий статус; жоден з них не має типу Nullable. Тому для практичних цілей у більшості сценаріїв слід проводити тестування на загальну концепцію дозволеності, ніж концепція Nullable, яка залежить від реалізації. Тому нам не слід зависати, зосередившись виключно на типі .NET Nullable, а навпаки, враховуючи наше розуміння його вимог та поведінки, у процесі зосередження уваги на загальній, практичній концепції дозволеності.


9

Відповідь Лаймана чудова і мені допомогла, однак є ще одна помилка, яку потрібно виправити.

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

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


2

Немає нічого вбудованого, про що я знаю, оскільки int?, тощо, є просто синтаксичним цукром Nullable<T>; і не отримує спеціального лікування, крім цього. Це особливо малоймовірно, якщо ви намагаєтесь отримати це на основі інформації про тип даного типу. Як правило, це завжди вимагає певного коду "прокрутити свій власний". Вам довелося б використовувати Reflection, щоб створити новий Nullableтип із параметром type типу введення.

Змінити: Як сказано в коментарі пропонують на насправді Nullable<> це лікування спеціально, і під час виконання для завантаження , як описано в цій статті .


По суті, я майже впевнений, що CLR має якусь особливу магію для обробки Nullable <> дещо інакше. Мені потрібно буде це перевірити.
TraumaPony

Мені б це цікаво, я з радістю визнаю, що я помиляюся, якщо це так :-)
ljs

Оскільки ви можете додати два int?за допомогою +оператора, ми знаємо, що вони Nullableотримують особливу обробку, оскільки такий тип загального перевантаження оператора інакше не спрацював би.
Конрад Рудольф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.