sha*_*are 11 c# reflection nullable nullable-reference-types
C#8.0引入了可为空的引用类型。这是一个具有可空属性的简单类:
public class Foo
{
public String? Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
有没有一种方法可以通过反射检查类属性是否使用可为空的引用类型?
Bil*_*ees 38
.NET 6 Preview 7 添加了反射 API 来获取可空性信息。
\n\n显然,这只能帮助那些针对 .NET 6+ 的人。
\n\n\n获取顶级可空性信息
\n想象一下您\xe2\x80\x99正在实现一个序列化器。使用这些新的 API,序列化程序可以检查给定属性是否可以设置为 null:
\nRun Code Online (Sandbox Code Playgroud)\nprivate NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext();\n\nprivate void DeserializePropertyValue(PropertyInfo p, object instance, object? value)\n{\n if (value is null)\n {\n var nullabilityInfo = _nullabilityContext.Create(p);\n if (nullabilityInfo.WriteState is not NullabilityState.Nullable)\n {\n throw new MySerializerException($"Property \'{p.GetType().Name}.{p.Name}\'\' cannot be set to null.");\n }\n }\n\n p.SetValue(instance, value);\n}\n
Ton*_*Nam 15
迟到的答复。
感谢 Bill Menees,这就是我最终使用的:
static bool IsMarkedAsNullable(PropertyInfo p)
{
return new NullabilityInfoContext().Create(p).WriteState is NullabilityState.Nullable;
}
Run Code Online (Sandbox Code Playgroud)
// 测试:
class Foo
{
public int Int1 { get; set; }
public int? Int2 { get; set; } = null;
public string Str1 { get; set; } = "";
public string? Str2 { get; set; } = null;
public List<Foo> Lst1 { get; set; } = new();
public List<Foo>? Lst2 { get; set; } = null;
public Dictionary<int, object> Dic1 { get; set; } = new();
public Dictionary<int, object>? Dic2 { get; set; } = null;
}
....
var props = typeof(Foo).GetProperties();
foreach(var prop in props)
{
Console.WriteLine($"Prop:{prop.Name} IsNullable:{IsMarkedAsNullable(prop)}");
}
// outputs:
Prop:Int1 IsNullable:False
Prop:Int2 IsNullable:True
Prop:Str1 IsNullable:False
Prop:Str2 IsNullable:True
Prop:Lst1 IsNullable:False
Prop:Lst2 IsNullable:True
Prop:Dic1 IsNullable:False
Prop:Dic2 IsNullable:True
Run Code Online (Sandbox Code Playgroud)
我编写了一个库来反射 NRT 类型 - 在内部它查看生成的属性并为您提供一个简单的 API:
https://github.com/RicoSuter/Namotion.Reflection
这似乎起作用,至少在我测试过的类型上起作用。
您需要传递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;
}
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参见此文档。
一般要点是,属性本身可以在其[Nullable]上具有属性,或者如果没有,则封闭类型可能具有[NullableContext]属性。我们首先寻找[Nullable],然后如果找不到,我们将[NullableContext]在封闭类型上寻找。
编译器可能会将属性嵌入到程序集中,并且由于我们可能正在查看来自不同程序集的类型,因此我们需要进行仅反射加载。
[Nullable]如果属性是通用的,则可以使用数组实例化。在这种情况下,第一个元素代表实际属性(其他元素代表通用参数)。[NullableContext]总是用单个字节实例化。
值的2意思是“可为空”。1表示“不可为空”,0表示“忽略”。