C# 如何使用.NET反射检查可为空的引用类型
C#8.0引入了可为空的引用类型。下面是一个具有可为null属性的简单类: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
公共类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]
如果属性是泛型的,则可以使用数组进行实例化。在本例中,第一个元素表示