Здесь есть две проблемы: 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, а должны включать наше понимание его требований и поведения в процессе сосредоточения внимания на общей практической концепции обнуляемости.