Как определить, может ли тип быть обнуляемым во время выполнения? - PullRequest
2 голосов
/ 21 июня 2019

Я пытаюсь определить, может ли тип быть обнуляемым или нет во время выполнения, чтобы преобразовать этот тип в соответствующий тип GraphQL, например, так:

С необязательными ссылочными типами включено :

  • string преобразуется в String!
  • string? преобразуется в String

С необязательными ссылочными типами отключено :

  • string преобразуется в String
  • NonNull<string> преобразуется в String! (NonNull - это пользовательский тип библиотеки)

У меня проблемы с адаптацией кода, который обнаружил обнуляемость типа:

bool isNullable = !typeInfo.IsValueType;

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

1 Ответ

2 голосов
/ 21 июня 2019

Обратите внимание, что существуют хорошие способы проверки «старых» обнуляемых типов, применимых к типам значений, которые подробно описаны здесь, в Переполнении стека.

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

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

Вопрекиназвание новой функции, обнуляемая ссылка types не о types , а о вещах , для которых используются эти типы.Эти вещи :

  • Поля
  • Свойства
  • Возвращаемые значения метода
  • Параметры метода

Теперь, конечно, это относится и к локальным переменным, но вам нужен целый «другой вид самоанализа», чтобы иметь дело с инструкциями по декодированию.Я не знаю, как, или даже, если этот вид информации закодирован в реальных инструкциях для локальных переменных.

ОК, так что теперь давайте посмотрим на некоторый код (кстати, яm для проверки всего этого LINQPad в экспериментальном режиме Roslyn):

public string? Nullable;
public string NonNullable;

Это два открытых поля.Проигнорируйте, является ли это хорошей идеей или нет.Как бы вы проверили тип этих полей и обнаружили наличие или отсутствие этого вопросительного знака?

Хорошо, давайте попробуем простой маршрут:

Type nullable = GetType().GetField("Nullable").FieldType;
Type nonNullable = GetType().GetField("NonNullable").FieldType;
Console.WriteLine(ReferenceEquals(nullable, nonNullable));

Запуск этого дает мне:

True

Так ясно, что это не работает.Объекты Type точно такие же экземпляр .Они не просто сравнивают равными , я получил то же самое, без разницы.По сути, FieldType не замечает наличия или отсутствия этого знака вопроса.

В моих комментариях выше есть некоторые из этих деталей, но главная причина этого в том, что все существующие пакеты nuget и скомпилированный код по-прежнему будут работатьс этой новой поддержкой из-за этого.Нет необходимости переписывать код для обработки чего-то вроде NullableReferenceType<T> внезапно. Это хорошая вещь , но это также означает, что вы все равно будете передавать нулевые ссылки и возвращать их из существующих пакетов nuget.

Хорошо, тогда как мы это обнаружим??Ответ заключается в том, что информация об обнуляемости не привязана к типу, как я упоминал выше, а к вещь , которая имеет тип, в данном случае поля.

Давайтепоказать атрибуты в этих полях (снова я использую LINQPad):

GetType().GetField("Nullable").GetCustomAttributes().Dump();
GetType().GetField("NonNullable").GetCustomAttributes().Dump();

Это дает такой вывод:

Nullable fields attributes

Каквы можете видеть здесь, поле Nullable имеет дополнительный атрибут NullableAttribute.Я должен признаться, что я не знаю, о чем этот другой атрибут, мне придется исследовать больше.

Этот атрибут NullableAttribute гораздо сложнее, чем этот простой пример, поскольку он имеет свойство коллекции сbool значений.Давайте рассмотрим немного более сложный пример:

public List<string>? Nullable1;
public List<string?>? Nullable2;

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

Вот некоторые размышления, чтобы посмотреть на эти коллекции:

GetType().GetField("Nullable1").GetCustomAttributesData().Dump();
GetType().GetField("Nullable2").GetCustomAttributesData().Dump();

и их вывод:

nullable generic types

Здесь вы можете видеть, что есть разница в втором элементе в этой коллекции (я «обвел» их красными ... прямоугольниками ...), я ожидаю, что первый элемент применяетсяк списку, второй к первому параметру универсального типа.Если у вас есть универсальные списки, содержащие универсальные типы, число параметров соответственно увеличится.

Вы также можете найти дополнительную информацию об этом отличном сообщении в блоге Rico Suter .

...