web-dev-qa-db-ja.com

.NETリフレクションを使用してnull可能な参照型を確認する方法

C#8.0では、null許容の参照型が導入されています。 null可能プロパティを持つ単純なクラスを次に示します。

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

クラスプロパティをチェックして、リフレクションを介してnull可能な参照型を使用する方法はありますか?

14
shadeglare

これは、少なくとも私がテストしたタイプでは機能するようです。

対象のプロパティのPropertyInfoと、そのプロパティが定義されているTypeを渡す必要があります(not派生型または親型-正確な型である必要があります):

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]は常に1バイトでインスタンス化されます。

2の値は「null可能」を意味します。 1は「null不可」を意味し、0は「気付かない」を意味します。

9
canton7