Как использовать .NET отражение для проверки обнуляемого ссылочного типа - PullRequest
11 голосов
/ 18 октября 2019

C # 8.0 вводит обнуляемые ссылочные типы. Вот простой класс с обнуляемым свойством:

public class Foo
{
    public String? Bar { get; set; }
}

Есть ли способ проверить, что свойство класса использует обнуляемый ссылочный тип через отражение?

1 Ответ

5 голосов
/ 18 октября 2019

Похоже, что это работает, по крайней мере, для типов, с которыми я его тестировал.

Вам нужно передать PropertyInfo для интересующего вас свойства, а также Type, которыйэто свойство определено для ( не производного или родительского типа - это должен быть точный тип):

public static bool IsNullable(Type enclosingType, PropertyInfo property)
{
    if (!enclosingType.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Contains(property))
        throw new ArgumentException("enclosingType must be the type which defines property");

    var nullable = property.CustomAttributes
        .FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
    if (nullable != null && nullable.ConstructorArguments.Count == 1)
    {
        var attributeArgument = nullable.ConstructorArguments[0];
        if (attributeArgument.ArgumentType == typeof(byte[]))
        {
            var args = (ReadOnlyCollection<CustomAttributeTypedArgument>)attributeArgument.Value;
            if (args.Count > 0 && args[0].ArgumentType == typeof(byte))
            {
                return (byte)args[0].Value == 2;
            }
        }
        else if (attributeArgument.ArgumentType == typeof(byte))
        {
            return (byte)attributeArgument.Value == 2;
        }
    }

    var context = enclosingType.CustomAttributes
        .FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
    if (context != null &&
        context.ConstructorArguments.Count == 1 &&
        context.ConstructorArguments[0].ArgumentType == typeof(byte))
    {
        return (byte)context.ConstructorArguments[0].Value == 2;
    }

    // Couldn't find a suitable attribute
    return false;
}

Подробнее см. в этом документе .

Общая суть в том, что либо у самого свойства может быть атрибут [Nullable], либо, если оно отсутствует, у включающего типа может быть атрибут [NullableContext]. Сначала мы ищем [Nullable], затем, если мы не найдем его, мы ищем [NullableContext] во включающем типе.

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

[Nullable] может быть создан с помощью массива, если свойство является универсальным. В этом случае первый элемент представляет фактическое свойство (а остальные элементы представляют общие аргументы). [NullableContext] всегда создается одним байтом.

Значение 2 означает «обнуляемый». 1 означает «не обнуляемый», а 0 означает «забывчивый».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...