Как проверить, является ли объект обнуляемым?

203

Как проверить, является ли данный объект обнуляемым, другими словами, как реализовать следующий метод ...

bool IsNullableValueType(object o)
{
    ...
}

РЕДАКТИРОВАТЬ: Я ищу обнуляемые типы значений. Я не имел в виду типы ссылок.

//Note: This is just a sample. The code has been simplified 
//to fit in a post.

public class BoolContainer
{
    bool? myBool = true;
}

var bc = new BoolContainer();

const BindingFlags bindingFlags = BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
                        ;


object obj;
object o = (object)bc;

foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
    obj = (object)fieldInfo.GetValue(o);
}

obj теперь ссылается на объект типа bool( System.Boolean) со значением, равным true. То, что я действительно хотел, было объектом типаNullable<bool>

Так что теперь, как обходной путь, я решил проверить, может ли o быть обнуляемым, и создать обнуляемую оболочку вокруг obj.

самоучка
источник
Должен ли код включать строки как обнуляемые? Это неуниверсальный ValueType, который выглядит обнуляемым. Или они не ValueType?
TamusJRoyce
Строка не является ValueType. Это ссылочный тип.
Suncat2000
Это действительно хороший вопрос! Type.IsNullableType () является своего рода обманом, потому что он на самом деле проверяет только тип, являющийся Nullable <T>, который не дал ожидаемых результатов, если вы на самом деле хотели проверить любые типы, которые могут принимать нуль значение (например, я пытался использовать с a.IsNullableType (), где 'a' было 'typeof (string)', определенным во время выполнения)
ErrCode
Ответ в fieldInfo.FieldType: проверьте, является ли FieldType универсальным и универсальный тип имеет тип Nullable <>. (Пример: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). Не пытайтесь получить obj.GetType (), он будет иметь UndelyingSystemType с переменной Nullable <T> T (в вашем случае с булевым типом вместо Nullable <Boolean>), это проблема с боксом.
SoLaR

Ответы:

272

Есть два типа обнуляемых - Nullable<T>и ссылочный тип.

Джон исправил меня, что трудно получить тип в штучной упаковке, но вы можете с помощью дженериков: - так как насчет ниже. На самом деле это тип тестирования T, но использование objпараметра исключительно для вывода обобщенного типа (чтобы его было проще вызывать) - objхотя он будет работать почти одинаково без параметра.

static bool IsNullable<T>(T obj)
{
    if (obj == null) return true; // obvious
    Type type = typeof(T);
    if (!type.IsValueType) return true; // ref-type
    if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
    return false; // value-type
}

Но это не будет работать так хорошо, если вы уже поместили значение в переменную объекта.

Документация Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type

Марк Гравелл
источник
7
Последняя строка действительна только в том случае, если вам каким-то образом удается получить Nullable <T> в штучной упаковке вместо того, чтобы прыгать прямо в T. Это возможно, но сложно достичь из того, что я помню.
Джон Скит
Этот код был полезен для меня не потому, что я получил Nullable <T> в штучной упаковке, а потому, что я писал базовый базовый класс конвертера WPF, а некоторые свойства обнуляются, поэтому я использовал Nullable.GetUnderlyingType для обнаружения этого случая и Activator.CreateInstance, чтобы сделать Nullable в штучной упаковке, (Convert.ChangeType не обрабатывает Nullables кстати).
Qwertie
1
@ Абель, если вы хотите изменить его, чтобы уточнить, что он не рассматривал ссылочные типы, я думаю , что мой ответ предшествовал этому редактированию; Я подозреваю, что читатель может принять собственное решение там, основываясь на своих собственных потребностях (я думаю, что он подтвердил: его комментарий re-type добавлен в 14:42; мой ответ был все <= 14:34)
Марк Гравелл
1
Будет ли (obj == null) генерировать исключение, когда obj = 1?
Ци Фан
3
@JustinMorgan Если Tэто общий параметр, ограниченный T : struct, то Tне допускается Nullable<>, так что вам не нужно проверять в этом случае! Я знаю, что тип Nullable<>является структурой, но в C # ограничение where T : structспециально исключает обнуляемые типы значений. В спецификации говорится: «Обратите внимание, что, хотя классифицируется как тип значения, обнуляемый тип (§4.1.10) не удовлетворяет ограничению типа значения».
Джепп Стиг Нильсен
46

Существует очень простое решение с использованием перегрузок методов.

http://deanchalk.com/is-it-nullable/

выдержка:

public static class ValueTypeHelper
{
    public static bool IsNullable<T>(T t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

затем

static void Main(string[] args)
{
    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    bool result1 = ValueTypeHelper.IsNullable(a); // false
    bool result2 = ValueTypeHelper.IsNullable(b); // true
    bool result3 = ValueTypeHelper.IsNullable(c); // false
    bool result4 = ValueTypeHelper.IsNullable(d); // false
    bool result5 = ValueTypeHelper.IsNullable(e); // true
    bool result6 = ValueTypeHelper.IsNullable(f); // true
Дин Мел
источник
7
плюс один для вас, сэр, для добавления тестовых случаев. Я использовал эти тесты для проверки всех остальных ответов. Больше людей должны пройти этот дополнительный бит.
Марти Нил
4
Для чего это стоит, это не работает в VB.NET. Это приводит к ошибке компилятора «Не удалось разрешить перегрузку, потому что недоступный« IsNullable »наиболее специфичен для этих аргументов » во всех ситуациях, где Trueбудет возвращено.
ckittel
1
Мне действительно нравится это решение - и это позор, VB не может справиться с этим. Я попытался поработать с ValueType, но столкнулся с проблемой несовместимости компилятора VB относительно того, какую перегрузку использовать в зависимости от того, был ли он вызван как общий метод или расширение, я даже поднял вопрос об этом, так как это кажется странным: stackoverflow.com/ Вопросы / 12319591 /…
Джеймс Клоуз
22
Вы проверяете тип времени компиляции , но уже очевидно (из intellisense), является ли тип времени компиляции nullable ( System.Nullable<>). Если вы говорите, object g = e;а затем ValueTypeHelper.IsNullable(g), что вы ожидаете получить?
Джепп Стиг Нильсен
18
Я только что проверил; это не работает , как сказал Джеппе. Если переменные приводятся к объекту, он всегда возвращает false. Таким образом, вы не можете определить тип неизвестного объекта во время выполнения таким образом. Это работает только в том случае, если тип зафиксирован во время компиляции, и в этом случае вам вообще не нужна проверка во время выполнения.
HugoRune
30

Вопрос "Как проверить, можно ли обнулять тип?" на самом деле «Как проверить, является ли тип Nullable<>?», что можно обобщить на «Как проверить, является ли тип составным типом некоторого универсального типа?», чтобы он не только отвечал на вопрос «Является Nullable<int>ли Nullable<>?», но и «Является ли ?».List<int>List<>

В большинстве представленных решений используется Nullable.GetUnderlyingType()метод, который, очевидно, будет работать только в случае Nullable<>. Я не видел общего рефлексивного решения, которое будет работать с любым родовым типом, поэтому я решил добавить его сюда для потомков, хотя на этот вопрос уже давным-давно был дан ответ.

Чтобы проверить, является ли тип какой-то формой Nullable<> использования отражения, сначала необходимо преобразовать созданный универсальный тип, например Nullable<int>, в определение универсального типа Nullable<>. Вы можете сделать это с помощью GetGenericTypeDefinition()метода Typeкласса. Затем вы можете сравнить полученный тип с Nullable<>:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

То же самое можно применить к любому универсальному типу:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

Несколько типов могут показаться одинаковыми, но разное количество аргументов типа означает, что это совершенно другой тип.

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

Поскольку Typeобъект создается один раз для каждого типа, вы можете проверить равенство ссылок между ними. Поэтому, если вы хотите проверить, имеют ли два объекта одно и то же определение универсального типа, вы можете написать:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

Если вы хотите проверить, является ли объект обнуляемым, а не a Type, то вы можете использовать описанную выше технику вместе с решением Марка Гравелла, чтобы создать довольно простой метод:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}
Аллон Гуралнек
источник
@ AllonGuralnek В моем ответе есть упрощенная версия. Я хотел сделать это как редактирование, и, поскольку моя репутация не соответствует вашему уровню, он будет редактироваться без моего имени в вашем ответе, несмотря на это, кажется, что обзор всегда бросает меня в ногу, что он обожает автора, даже если он был не. Странный мир, некоторые люди не получают определения :).
ipavlu
@ipavlu: Ваша версия не упрощена, на самом деле она более сложная. Я думаю, вы имеете в виду, что это оптимизировано, так как вы кешируете результат. Это усложняет понимание.
Аллон Гуралнек
@ AllonGuralnek статический универсальный класс и статические однократно инициализированные поля, что сложно? Боже мой, я совершил ужасное преступление :).
ipavlu
@ipavku: Да, потому что это не имеет ничего общего с вопросом «Как проверить, можно ли обнулить объект?». Я стараюсь сделать это простым и конкретным, и я избегаю вводить ненужные и не связанные понятия.
Аллон Гуралнек
1
@nawfal: Если я вас правильно понял, вы задаете вопрос о моей реализации перед лицом того, Nullable.GetUnderlyingType()что уже есть в фреймворке. Почему бы просто не использовать метод в рамках? Ну, ты должен. Это более ясно, более кратко и лучше проверено. Но в своем посте я пытаюсь научить, как использовать рефлексию для получения необходимой информации, чтобы кто-то мог применить ее к любому типу (заменив typeof(Nullable<>)любым другим типом). Если вы посмотрите на источники GetUnderlyingType()(оригинальные или декомпилированные), вы увидите, что это очень похоже на мой код.
Аллон Гуралнек
30

Это работает для меня и кажется простым:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

Для типов значений:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}
Erik
источник
7
Для чего это стоит, это также тест, используемый Microsoft
canton7
1
Хорошо ... Разве это не лучший ответ, потому что он пришел позже? Я нахожу топовый ответ таким странным.
Винсент Бускарелло
1
Это должен быть главный ответ. После нескольких дней попыток использовать разные методы, я случайно подумал об этом решении, попробовал его, и, похоже, оно работает отлично (по сравнению с ответом с самым высоким рейтингом)
user3163495
2
Это отличное решение, чтобы выяснить, может ли любой экземпляр быть установлен в NULL, но он вернет true для всего, что может быть установлено в null, включая обычные объекты. Важно понимать, что исходный вопрос специально хотел обнаружить Nullable ValueTypes.
JamesHoux
20

Ну, вы можете использовать:

return !(o is ValueType);

... но сам объект не может быть обнуляемым или нет - тип есть. Как вы планировали использовать это?

Джон Скит
источник
2
Это немного сбило меня с толку. например, int? я = 5; typeof (i) возвращает System.Int32 вместо Nullable <Int32> - typeof (int?) возвращает Nullable <Int32> .. где я могу получить некоторую ясность по этой теме?
Гишу
2
typeof (i) выдаст ошибку компилятора - вы не можете использовать typeof с переменной. Что ты на самом деле делал?
Джон Скит
15
Сначала i.GetType () будет боксировать с Object, и такого понятия, как упакованный в Nullable тип, не существует - Nullable <int> помещается в пустую ссылку или упакованный int.
Джон Скит
Этот способ лучше, чем Nullable.GetUnderlyingType (type)! = Null?
Kiquenet
@Kiquenet: Мы не имеем типа здесь - только значение.
Джон Скит
11

Самый простой способ понять это:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
КАРЛОС ЛОТ
источник
+1. Отличное решение для штучных нулевых типов. Я еще не проверял это специально. Так что если кто-то еще может проверить, это будет оценено.
TamusJRoyce
Я уже проверил это. Я должен был создать некий Nullableтип, но с другой семантикой. В моей ситуации я должен поддерживать nullкак допустимое значение, а также вообще не поддерживать значение. Итак, создан Optionalтип. Поскольку было необходимо поддерживать nullзначения, мне также пришлось реализовать код для обработки Nullableзначений как часть моей реализации. Вот откуда появился этот код.
КАРЛОС ЛОТ
9
Я думаю, что это решение неверно. Передача типа значения Nullable в качестве аргумента методу, ожидающему параметр объекта типа, должен вызвать бокс. Nullable - это тип значения, а результат преобразования в бокс - это ссылочный тип. Там нет в штучной упаковке Nullables. Я считаю, что этот метод всегда возвращает ложь?
Mishax
1
Любой тест об этом, как другие ответы?
Kiquenet
5
Это не работает из-за стоимости бокса. Он всегда вернет FALSE.
N Rocking
10

Здесь есть две проблемы: 1) тестирование, чтобы увидеть, является ли Тип обнуляемым; и 2) тестирование, чтобы увидеть, представляет ли объект обнуляемый тип.

Для выпуска 1 (тестирование Type) вот решение, которое я использовал в своих собственных системах: решение TypeIsNullable-check

Для проблемы 2 (тестирование объекта) решение Дина Чака, описанное выше, работает для типов значений, но не работает для ссылочных типов, поскольку перегрузка <T> всегда возвращает false. Поскольку ссылочные типы по своей природе обнуляются, тестирование ссылочного типа всегда должно возвращать true. Пожалуйста, смотрите примечание [О «обнуляемости»] ниже для объяснения этой семантики. Итак, вот моя модификация подхода Дина:

    public static bool IsObjectNullable<T>(T obj)
    {
        // If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
        if (!typeof(T).IsValueType || obj == null)
            return true;

        // Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
        return false; 
    }

    public static bool IsObjectNullable<T>(T? obj) where T : struct
    {
        // Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
        return true;
    }

А вот моя модификация кода клиентского теста для вышеуказанного решения:

    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    string g = "something";

    bool isnullable = IsObjectNullable(a); // false 
    isnullable = IsObjectNullable(b); // true 
    isnullable = IsObjectNullable(c); // true 
    isnullable = IsObjectNullable(d); // true 
    isnullable = IsObjectNullable(e); // true 
    isnullable = IsObjectNullable(f); // true 
    isnullable = IsObjectNullable(g); // true

Причина, по которой я изменил подход Дина в IsObjectNullable <T> (T t), заключается в том, что его оригинальный подход всегда возвращал false для ссылочного типа. Поскольку такой метод, как IsObjectNullable, должен иметь возможность обрабатывать значения ссылочного типа и поскольку все ссылочные типы по своей природе могут иметь значение null, то если передается либо ссылочный тип, либо значение null, метод всегда должен возвращать true.

Вышеупомянутые два метода могут быть заменены следующим единственным методом и получить тот же результат:

    public static bool IsObjectNullable<T>(T obj)
    {
        Type argType = typeof(T);
        if (!argType.IsValueType || obj == null)
            return true;
        return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

Однако проблема этого последнего подхода, состоящего из одного метода, заключается в том, что производительность снижается при использовании параметра Nullable <T>. Для выполнения последней строки этого единственного метода требуется гораздо больше процессорного времени, чем для того, чтобы компилятор мог выбрать перегрузку второго метода, показанную ранее, когда параметр Nullable <T> -типа используется в вызове IsObjectNullable. Поэтому оптимальным решением является использование двухметодного подхода, показанного здесь.

CAVEAT: этот метод работает надежно, только если вызывается с использованием исходной ссылки на объект или точной копии, как показано в примерах. Однако если обнуляемый объект помещается в другой тип (например, объект и т. Д.) Вместо того, чтобы оставаться в исходной форме Nullable <>, этот метод не будет работать надежно. Если код, вызывающий этот метод, не использует исходную, распакованную ссылку на объект или точную копию, он не может надежно определить обнуляемость объекта с помощью этого метода.

В большинстве сценариев кодирования для определения обнуляемости вместо этого нужно полагаться на тестирование Типа исходного объекта, а не его ссылки (например, код должен иметь доступ к исходному Типу объекта для определения обнуляемости). В этих более распространенных случаях IsTypeNullable (см. Ссылку) является надежным методом определения обнуляемости.

PS - про "обнуляемость"

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

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

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

Марк Джонс
источник
8

Самое простое решение, которое я придумал, - это реализовать решение Microsoft ( Как: определить тип Nullable (Руководство по программированию в C #) ) в качестве метода расширения:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Это можно затем назвать так:

bool isNullable = typeof(int).IsNullable();

Это также кажется логичным способом доступа, IsNullable()потому что он вписывается во все другие IsXxxx()методы Typeкласса.

sclarke81
источник
1
Разве вы не хотите использовать "==" вместо "! ="?
Вкельман
Хорошее место @vkelman Вместо того, чтобы внести это изменение, я обновил ответ, чтобы использовать текущее предложение от Microsoft, поскольку это изменилось с тех пор, как я написал это.
sclarke81
6

Будьте осторожны, когда упаковываете обнуляемый тип ( Nullable<int>или int? Например):

int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);

int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))

Он становится истинным ссылочным типом, поэтому вы теряете тот факт, что он был обнуляемым.

thinkbeforecoding
источник
3

Может быть, немного не по теме, но все же некоторая интересная информация. Я нахожу много людей, которые используют Nullable.GetUnderlyingType() != nullдля идентификации, если тип обнуляемый. Это, очевидно, работает, но Microsoft рекомендует следующее type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(см. Http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

Я посмотрел на это с точки зрения производительности. Заключение теста (один миллион попыток), приведенное ниже, заключается в том, что, когда тип является обнуляемым, опция Microsoft обеспечивает наилучшую производительность.

Nullable.GetUnderlyingType (): 1335 мс (в 3 раза медленнее)

GetGenericTypeDefinition () == typeof (Nullable <>): 500 мс

Я знаю, что мы говорим о небольшом количестве времени, но все любят подправлять миллисекунды :-)! Так что, если ваш начальник хочет, чтобы вы сократили несколько миллисекунд, тогда это ваш спаситель ...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
    {
        Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
    }

    Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
   {
       Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
   }

   Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
   stopwatch.Stop();
}
Роэль ван Меген
источник
1
Привет, вероятно, есть одна проблема с измерением времени, Assert может повлиять на результаты. Вы тестировали без Assert? Также Console.WriteLine должен находиться за пределами области измерения. +1 за попытку количественно оценить проблемы с производительностью :).
ipavlu
@ipavlu Console.WriteLineдействительно вне зоны измерения;)
nawfal
Роэл, как отметил Ипавлу, Assertдолжен быть вне петли. Во-вторых, вы должны также проверить его на ненулевые значения, чтобы проверить на ложные случаи. Я сделал аналогичный тест (2 nulables и 4 nonululbles), и я получаю ~ 2 секунды для GetUnderlyingTypeи ~ 1 секунду для GetGenericTypeDefinition, т.е. GetGenericTypeDefinitionэто в два раза быстрее (не трижды).
nawfal
Сделал еще один раунд с 2 обнуляемыми и 2 не обнуляемыми - на этот раз GetUnderlyingTypeбыл в 2,5 раза медленнее. Только с ненулевыми значениями - на этот раз шея и шея.
Nawfal
Но что еще более важно, GetUnderlyingTypeполезно, когда вам нужно проверить на обнуляемость и получить базовый тип, если он обнуляем. Это очень полезно, и вы часто видите шаблоны Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). Это как asключевое слово, проверяет приведение, а также делает это и возвращает результат. Если вы хотите вернуть базовый тип nullable обратно, тогда GetGenericTypeDefinitionпроверка, а затем получение универсального типа будет плохой идеей. Также GetUnderlyingTypeгораздо более читабельным и запоминающимся. Я не против, если я делаю это только ~ 1000 раз.
Nawfal
0

Эта версия:

  • кеширование результатов происходит быстрее,
  • не требует ненужных переменных, таких как Method (T obj)
  • НЕ СЛОЖНО :),
  • просто статический универсальный класс, который имеет одноразовые вычисляемые поля

:

public static class IsNullable<T>
{
    private static readonly Type type = typeof(T);
    private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    public static bool Result { get { return is_nullable; } }
}

bool is_nullable = IsNullable<int?>.Result;
ipavlu
источник
Я думаю, что вы ответили себе статическим объявлением is_nullable. Совет: объявлять объекты с помощью int? (возьмите a = (int?) 8;) и посмотрите, что произойдет.
SoLaR
0

Вот то, что я придумал, так как все остальное казалось неудачным - по крайней мере, в ПЛК - Portable Class Library / .NET Core с> = C # 6

Решение: Расширьте статические методы для любого Типа Tи Nullable<T>используйте тот факт, что будет вызван метод статического расширения, соответствующий базовому типу, и он будет иметь приоритет над общимT методом расширения.

Для T:

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

и для Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

Использование Reflection и type.IsGenericType... не работало на моем текущем наборе .NET Runtime. Также не помогла документация MSDN .

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

Частично потому, что API-интерфейс Reflection значительно изменился в .NET Core.

Лоренц Ло Зауэр
источник
0

Я думаю, что те, которые используют предложенное Microsoft тестирование IsGenericType, хороши, но в коде GetUnderlyingTypeMicrosoft использует дополнительный тест, чтобы убедиться, что вы не прошли определение универсального типа Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
NetMage
источник
-1

простой способ сделать это:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

это мои юнит-тесты и все пройдено

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

фактические юнит-тесты

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }
VJPPaz
источник