C# 如何使用.NET反射检查可为空的引用类型

C# 如何使用.NET反射检查可为空的引用类型,c#,reflection,nullable,nullable-reference-types,C#,Reflection,Nullable,Nullable Reference Types,C#8.0引入了可为空的引用类型。下面是一个具有可为null属性的简单类: 公共类Foo { 公共字符串?Bar{get;set;} } 有没有办法通过反射检查类属性是否使用了可为空的引用类型?至少在我测试过的类型上是这样 public static bool IsNullable(PropertyInfo property) => IsNullableHelper(property.PropertyType, property.DeclaringType, property.C

C#8.0引入了可为空的引用类型。下面是一个具有可为null属性的简单类:

公共类Foo
{
公共字符串?Bar{get;set;}
}

有没有办法通过反射检查类属性是否使用了可为空的引用类型?

至少在我测试过的类型上是这样

public static bool IsNullable(PropertyInfo property) =>
    IsNullableHelper(property.PropertyType, property.DeclaringType, property.CustomAttributes);

public static bool IsNullable(FieldInfo field) =>
    IsNullableHelper(field.FieldType, field.DeclaringType, field.CustomAttributes);

public static bool IsNullable(ParameterInfo parameter) =>
    IsNullableHelper(parameter.ParameterType, parameter.Member, parameter.CustomAttributes);

private static bool IsNullableHelper(Type memberType, MemberInfo? declaringType, IEnumerable<CustomAttributeData> customAttributes)
{
    if (memberType.IsValueType)
        return Nullable.GetUnderlyingType(memberType) != null;

    var nullable = 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;
        }
    }

    for (var type = declaringType; type != null; type = type.DeclaringType)
    {
        var context = type.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;
}
publicstaticbool可为空(PropertyInfo属性)=>
IsNullableHelper(property.PropertyType、property.DeclaringType、property.CustomAttributes);
公共静态bool可为空(FieldInfo字段)=>
IsNullableHelper(field.FieldType、field.DeclaringType、field.CustomAttributes);
公共静态bool为空(ParameterInfo参数)=>
IsNullableHelper(parameter.ParameterType、parameter.Member、parameter.CustomAttributes);
私有静态bool IsNullableHelper(类型memberType、MemberInfo?declaringType、IEnumerable customAttributes)
{
if(memberType.IsValueType)
返回Nullable.GetUnderlineType(memberType)!=null;
var nullable=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(字节[])
{
var args=(ReadOnlyCollection)attributeArgument.Value!;
如果(args.Count>0&&args[0]。ArgumentType==typeof(byte))
{
返回(字节)参数[0]。值!==2;
}
}
else if(attributeArgument.ArgumentType==类型(字节))
{
返回(字节)attributeArgument.Value!==2;
}
}
for(变量类型=去极化类型;类型!=null;类型=类型。去极化类型)
{
var context=type.CustomAttributes
.FirstOrDefault(x=>x.AttributeType.FullName==“System.Runtime.CompilerServices.NullableContextAttribute”);
if(上下文!=null&&
context.ConstructorArguments.Count==1&&
context.ConstructorArguments[0]。ArgumentType==typeof(字节))
{
返回(字节)上下文。构造函数参数[0]。值!==2;
}
}
//找不到合适的属性
返回false;
}
有关详细信息,请参阅

一般要点是,属性本身可以有一个
[Nullable]
属性,如果没有,则封闭类型可能有
[NullableContext]
属性。我们首先查找
[Nullable]
,如果找不到它,则在封闭类型上查找
[NullableContext]

编译器可能会将属性嵌入到程序集中,因为我们可能会查看来自不同程序集中的类型,所以我们需要执行仅反射加载

[Nullable]
如果属性是泛型的,则可以使用数组进行实例化。在本例中,第一个元素表示实际属性(其他元素表示通用参数)<代码>[NullableContext]始终使用单个字节实例化


2
表示“可为空”
1
表示“不可为空”,而
0
表示“不可为空”。

至少在我测试过的类型上,这似乎是可行的

public static bool IsNullable(PropertyInfo property) =>
    IsNullableHelper(property.PropertyType, property.DeclaringType, property.CustomAttributes);

public static bool IsNullable(FieldInfo field) =>
    IsNullableHelper(field.FieldType, field.DeclaringType, field.CustomAttributes);

public static bool IsNullable(ParameterInfo parameter) =>
    IsNullableHelper(parameter.ParameterType, parameter.Member, parameter.CustomAttributes);

private static bool IsNullableHelper(Type memberType, MemberInfo? declaringType, IEnumerable<CustomAttributeData> customAttributes)
{
    if (memberType.IsValueType)
        return Nullable.GetUnderlyingType(memberType) != null;

    var nullable = 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;
        }
    }

    for (var type = declaringType; type != null; type = type.DeclaringType)
    {
        var context = type.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;
}
publicstaticbool可为空(PropertyInfo属性)=>
IsNullableHelper(property.PropertyType、property.DeclaringType、property.CustomAttributes);
公共静态bool可为空(FieldInfo字段)=>
IsNullableHelper(field.FieldType、field.DeclaringType、field.CustomAttributes);
公共静态bool为空(ParameterInfo参数)=>
IsNullableHelper(parameter.ParameterType、parameter.Member、parameter.CustomAttributes);
私有静态bool IsNullableHelper(类型memberType、MemberInfo?declaringType、IEnumerable customAttributes)
{
if(memberType.IsValueType)
返回Nullable.GetUnderlineType(memberType)!=null;
var nullable=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(字节[])
{
var args=(ReadOnlyCollection)attributeArgument.Value!;
如果(args.Count>0&&args[0]。ArgumentType==typeof(byte))
{
返回(字节)参数[0]。值!==2;
}
}
else if(attributeArgument.ArgumentType==类型(字节))
{
返回(字节)attributeArgument.Value!==2;
}
}
for(变量类型=去极化类型;类型!=null;类型=类型。去极化类型)
{
var context=type.CustomAttributes
.FirstOrDefault(x=>x.AttributeType.FullName==“System.Runtime.CompilerServices.NullableContextAttribute”);
if(上下文!=null&&
context.ConstructorArguments.Count==1&&
context.ConstructorArguments[0]。ArgumentType==typeof(字节))
{
返回(字节)上下文。构造函数参数[0]。值!==2;
}
}
//找不到合适的属性
返回false;
}
有关详细信息,请参阅

一般要点是,属性本身可以有一个
[Nullable]
属性,如果没有,则封闭类型可能有
[NullableContext]
属性。我们首先查找
[Nullable]
,如果找不到它,则在封闭类型上查找
[NullableContext]

编译器可能会将属性嵌入到程序集中,因为我们可能会查看来自不同程序集中的类型,所以我们需要执行仅反射加载

[Nullable]
如果属性是泛型的,则可以使用数组进行实例化。在本例中,第一个元素表示