Как проверить, является ли данный объект обнуляемым, другими словами, как реализовать следующий метод ...
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.
Ответы:
Есть два типа обнуляемых -
Nullable<T>
и ссылочный тип.Джон исправил меня, что трудно получить тип в штучной упаковке, но вы можете с помощью дженериков: - так как насчет ниже. На самом деле это тип тестирования
T
, но использованиеobj
параметра исключительно для вывода обобщенного типа (чтобы его было проще вызывать) -obj
хотя он будет работать почти одинаково без параметра.Но это не будет работать так хорошо, если вы уже поместили значение в переменную объекта.
Документация Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type
источник
T
это общий параметр, ограниченныйT : struct
, тоT
не допускаетсяNullable<>
, так что вам не нужно проверять в этом случае! Я знаю, что типNullable<>
является структурой, но в C # ограничениеwhere T : struct
специально исключает обнуляемые типы значений. В спецификации говорится: «Обратите внимание, что, хотя классифицируется как тип значения, обнуляемый тип (§4.1.10) не удовлетворяет ограничению типа значения».Существует очень простое решение с использованием перегрузок методов.
http://deanchalk.com/is-it-nullable/
выдержка:
затем
источник
True
будет возвращено.System.Nullable<>
). Если вы говорите,object g = e;
а затемValueTypeHelper.IsNullable(g)
, что вы ожидаете получить?Вопрос "Как проверить, можно ли обнулять тип?" на самом деле «Как проверить, является ли тип
Nullable<>
?», что можно обобщить на «Как проверить, является ли тип составным типом некоторого универсального типа?», чтобы он не только отвечал на вопрос «ЯвляетсяNullable<int>
лиNullable<>
?», но и «Является ли ?».List<int>
List<>
В большинстве представленных решений используется
Nullable.GetUnderlyingType()
метод, который, очевидно, будет работать только в случаеNullable<>
. Я не видел общего рефлексивного решения, которое будет работать с любым родовым типом, поэтому я решил добавить его сюда для потомков, хотя на этот вопрос уже давным-давно был дан ответ.Чтобы проверить, является ли тип какой-то формой
Nullable<>
использования отражения, сначала необходимо преобразовать созданный универсальный тип, напримерNullable<int>
, в определение универсального типаNullable<>
. Вы можете сделать это с помощьюGetGenericTypeDefinition()
методаType
класса. Затем вы можете сравнить полученный тип сNullable<>
:То же самое можно применить к любому универсальному типу:
Несколько типов могут показаться одинаковыми, но разное количество аргументов типа означает, что это совершенно другой тип.
Поскольку
Type
объект создается один раз для каждого типа, вы можете проверить равенство ссылок между ними. Поэтому, если вы хотите проверить, имеют ли два объекта одно и то же определение универсального типа, вы можете написать:Если вы хотите проверить, является ли объект обнуляемым, а не a
Type
, то вы можете использовать описанную выше технику вместе с решением Марка Гравелла, чтобы создать довольно простой метод:источник
Nullable.GetUnderlyingType()
что уже есть в фреймворке. Почему бы просто не использовать метод в рамках? Ну, ты должен. Это более ясно, более кратко и лучше проверено. Но в своем посте я пытаюсь научить, как использовать рефлексию для получения необходимой информации, чтобы кто-то мог применить ее к любому типу (заменивtypeof(Nullable<>)
любым другим типом). Если вы посмотрите на источникиGetUnderlyingType()
(оригинальные или декомпилированные), вы увидите, что это очень похоже на мой код.Это работает для меня и кажется простым:
Для типов значений:
источник
Ну, вы можете использовать:
... но сам объект не может быть обнуляемым или нет - тип есть. Как вы планировали использовать это?
источник
Самый простой способ понять это:
источник
Nullable
тип, но с другой семантикой. В моей ситуации я должен поддерживатьnull
как допустимое значение, а также вообще не поддерживать значение. Итак, созданOptional
тип. Поскольку было необходимо поддерживатьnull
значения, мне также пришлось реализовать код для обработкиNullable
значений как часть моей реализации. Вот откуда появился этот код.Здесь есть две проблемы: 1) тестирование, чтобы увидеть, является ли Тип обнуляемым; и 2) тестирование, чтобы увидеть, представляет ли объект обнуляемый тип.
Для выпуска 1 (тестирование Type) вот решение, которое я использовал в своих собственных системах: решение TypeIsNullable-check
Для проблемы 2 (тестирование объекта) решение Дина Чака, описанное выше, работает для типов значений, но не работает для ссылочных типов, поскольку перегрузка <T> всегда возвращает false. Поскольку ссылочные типы по своей природе обнуляются, тестирование ссылочного типа всегда должно возвращать true. Пожалуйста, смотрите примечание [О «обнуляемости»] ниже для объяснения этой семантики. Итак, вот моя модификация подхода Дина:
А вот моя модификация кода клиентского теста для вышеуказанного решения:
Причина, по которой я изменил подход Дина в IsObjectNullable <T> (T t), заключается в том, что его оригинальный подход всегда возвращал false для ссылочного типа. Поскольку такой метод, как IsObjectNullable, должен иметь возможность обрабатывать значения ссылочного типа и поскольку все ссылочные типы по своей природе могут иметь значение null, то если передается либо ссылочный тип, либо значение null, метод всегда должен возвращать true.
Вышеупомянутые два метода могут быть заменены следующим единственным методом и получить тот же результат:
Однако проблема этого последнего подхода, состоящего из одного метода, заключается в том, что производительность снижается при использовании параметра Nullable <T>. Для выполнения последней строки этого единственного метода требуется гораздо больше процессорного времени, чем для того, чтобы компилятор мог выбрать перегрузку второго метода, показанную ранее, когда параметр Nullable <T> -типа используется в вызове IsObjectNullable. Поэтому оптимальным решением является использование двухметодного подхода, показанного здесь.
CAVEAT: этот метод работает надежно, только если вызывается с использованием исходной ссылки на объект или точной копии, как показано в примерах. Однако если обнуляемый объект помещается в другой тип (например, объект и т. Д.) Вместо того, чтобы оставаться в исходной форме Nullable <>, этот метод не будет работать надежно. Если код, вызывающий этот метод, не использует исходную, распакованную ссылку на объект или точную копию, он не может надежно определить обнуляемость объекта с помощью этого метода.
В большинстве сценариев кодирования для определения обнуляемости вместо этого нужно полагаться на тестирование Типа исходного объекта, а не его ссылки (например, код должен иметь доступ к исходному Типу объекта для определения обнуляемости). В этих более распространенных случаях IsTypeNullable (см. Ссылку) является надежным методом определения обнуляемости.
PS - про "обнуляемость"
Я должен повторить заявление об обнуляемости, которое я сделал в отдельном посте, который относится непосредственно к правильному решению этой темы. То есть, я полагаю, что основное внимание здесь должно быть сосредоточено не на том, как проверить, является ли объект универсальным типом Nullable, а на том, можно ли присвоить значение null объекту его типа. Другими словами, я думаю, что мы должны определить, является ли тип объекта обнуляемым, а не обнуляемым. Разница заключается в семантике, а именно в практических причинах определения обнуляемости, которая обычно имеет значение.
В системе, использующей объекты с типами, которые могут быть неизвестны до времени выполнения (веб-службы, удаленные вызовы, базы данных, каналы и т. Д.), Общим требованием является определение того, можно ли присвоить объекту нулевое значение или объект может содержать ноль. Выполнение таких операций над ненулевыми типами, вероятно, приведет к ошибкам, обычно исключениям, которые очень дороги как с точки зрения производительности, так и требований к кодированию. Чтобы принять чрезвычайно предпочтительный подход к упреждающему избеганию таких проблем, необходимо определить, способен ли объект произвольного типа содержать нуль; то есть, является ли это вообще 'обнуляемым'.
В очень практичном и типичном смысле обнуляемость в терминах .NET вовсе не обязательно означает, что тип объекта является формой обнуляемого значения. Фактически, во многих случаях объекты имеют ссылочные типы, могут содержать нулевое значение и, следовательно, могут иметь значение NULL; ни один из них не имеет типа Nullable. Следовательно, для практических целей в большинстве сценариев тестирование должно проводиться для общей концепции обнуляемости, в отличие от концепции Nullable, зависящей от реализации. Таким образом, мы не должны зацикливаться на том, чтобы сосредоточиться исключительно на типе .NET Nullable, а должны включать наше понимание его требований и поведения в процессе сосредоточения внимания на общей практической концепции обнуляемости.
источник
Самое простое решение, которое я придумал, - это реализовать решение Microsoft ( Как: определить тип Nullable (Руководство по программированию в C #) ) в качестве метода расширения:
Это можно затем назвать так:
Это также кажется логичным способом доступа,
IsNullable()
потому что он вписывается во все другиеIsXxxx()
методыType
класса.источник
Будьте осторожны, когда упаковываете обнуляемый тип (
Nullable<int>
или int? Например):Он становится истинным ссылочным типом, поэтому вы теряете тот факт, что он был обнуляемым.
источник
Может быть, немного не по теме, но все же некоторая интересная информация. Я нахожу много людей, которые используют
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 мс
Я знаю, что мы говорим о небольшом количестве времени, но все любят подправлять миллисекунды :-)! Так что, если ваш начальник хочет, чтобы вы сократили несколько миллисекунд, тогда это ваш спаситель ...
источник
Console.WriteLine
действительно вне зоны измерения;)Assert
должен быть вне петли. Во-вторых, вы должны также проверить его на ненулевые значения, чтобы проверить на ложные случаи. Я сделал аналогичный тест (2 nulables и 4 nonululbles), и я получаю ~ 2 секунды дляGetUnderlyingType
и ~ 1 секунду дляGetGenericTypeDefinition
, т.е.GetGenericTypeDefinition
это в два раза быстрее (не трижды).GetUnderlyingType
был в 2,5 раза медленнее. Только с ненулевыми значениями - на этот раз шея и шея.GetUnderlyingType
полезно, когда вам нужно проверить на обнуляемость и получить базовый тип, если он обнуляем. Это очень полезно, и вы часто видите шаблоныActivator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)
. Это какas
ключевое слово, проверяет приведение, а также делает это и возвращает результат. Если вы хотите вернуть базовый тип nullable обратно, тогдаGetGenericTypeDefinition
проверка, а затем получение универсального типа будет плохой идеей. ТакжеGetUnderlyingType
гораздо более читабельным и запоминающимся. Я не против, если я делаю это только ~ 1000 раз.Эта версия:
:
источник
Вот то, что я придумал, так как все остальное казалось неудачным - по крайней мере, в ПЛК - Portable Class Library / .NET Core с> = C # 6
Решение: Расширьте статические методы для любого Типа
T
иNullable<T>
используйте тот факт, что будет вызван метод статического расширения, соответствующий базовому типу, и он будет иметь приоритет над общимT
методом расширения.Для
T
:и для
Nullable<T>
Использование Reflection и
type.IsGenericType
... не работало на моем текущем наборе .NET Runtime. Также не помогла документация MSDN .if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}
Частично потому, что API-интерфейс Reflection значительно изменился в .NET Core.
источник
Я думаю, что те, которые используют предложенное Microsoft тестирование
IsGenericType
, хороши, но в кодеGetUnderlyingType
Microsoft использует дополнительный тест, чтобы убедиться, что вы не прошли определение универсального типаNullable<>
:источник
простой способ сделать это:
это мои юнит-тесты и все пройдено
фактические юнит-тесты
источник