使用反射确定 C# 中引用类型的可空性
Determine Nullability Of A Reference Type in C# Using Reflection
我有一个使用 C#-8 并启用了可空类型的 .NET Core 项目。
我有以下class
public class MyClass
{
public int? NullableInt { get; private set; }
public string? NullableString { get; private set; }
public string NonNullableString { get; private set; }
public MySubClass? MyNullableSubClass { get; private set; }
}
我需要能够遍历所有属性 class 并确定哪些属性可为空。
所以我的代码看起来像这样
public IEnumerable<string> GetNullableProperties(Type type)
{
var nullableProperties = new List<string>();
foreach (var property in type.GetProperties())
{
var isNullable = false;
if (property.PropertyType.IsValueType)
{
isNullable = Nullable.GetUnderlyingType(property.PropertyType) != null;
} else {
var nullableAttribute = property.PropertyType.CustomAttributes
.FirstOrDefault(a => a.AttributeType.Name == "NullableAttribute");
isNullable = nullableAttribute != null;
}
if (isNullable)
{
nullableProperties.Add(property.propertyType.Name)
}
}
return nullableProperties;
}
正在将 MyClass
的类型传递给此方法 returns ["NullableInt", "NullableString", "NonNullableString", "MyNullableSubClass"]
.
然而,预期的 return 值为 ["NullableInt", "NullableString", "MyNullableSubClass"]
。
NonNullableString
属性 之所以被确定为可空,是因为它具有 Nullable 属性。
我的理解是判断一个引用类型是否为nullable时,需要看它是否具有Nullable属性。但是,字符串类型似乎并非如此。似乎所有字符串都定义了可为空的属性。有没有办法找出 string
是否可以为空(即使用可空运算符 ?
定义)。
您需要检查 属性 本身的自定义属性,而不是 属性 的类型。
public IEnumerable<string> GetNullableProperties(Type type)
{
var nullableProperties = new List<string>();
foreach (var property in type.GetProperties())
{
var isNullable = false;
if (property.PropertyType.IsValueType)
{
isNullable = Nullable.GetUnderlyingType(property.PropertyType) != null;
}
else
{
var nullableAttribute = property.CustomAttributes
.FirstOrDefault(a => a.AttributeType.Name == "NullableAttribute");
isNullable = nullableAttribute == null;
}
if (isNullable)
{
nullableProperties.Add(property.Name);
}
}
return nullableProperties;
}
此外,如果 属性 可以为 null,则此属性未定义。如果 属性 不可为空,则此属性存在。
我找到了完整的解决方案。我们需要检查 属性 和 class.
上的自定义属性
...
private const byte NonNullableContextValue = 1;
private const byte NullableContextValue = 2;
public IEnumerable<string> GetNullableProperties(Type type)
{
foreach (var property in type.GetProperties())
{
var isNullable = property.PropertyType.IsValueType
? Nullable.GetUnderlyingType(property.PropertyType) != null;
: IsReferenceTypePropertyNullable(property);
if (isNullable)
{
nullableProperties.Add(property.propertyType.Name)
}
}
return nullableProperties;
}
private function bool IsReferenceTypePropertyNullable(PropertyInfo property)
{
var classNullableContextAttribute = property.DeclaringType.CustomerProperties
.FirstOrDefault(c => c.AttributeType.Name == "NullableContextAttribute")
var classNullableContext = classNullableContextAttribute
?.ConstructorArguments
.First(ca => ca.ArgumentType.Name == "Byte")
.Value;
// EDIT: This logic is not correct for nullable generic types
var propertyNullableContext = property.CustomAttributes
.FirstOrDefault(c => c.AttributeType.Name == "NullableAttribute")
?.ConstructorArguments
.First(ca => ca.ArgumentType.Name == "Byte")
.Value;
// If the property does not have the nullable attribute then it's
// nullability is determined by the declaring class
propertyNullableContext ??= classNullableContext;
// If NullableContextAttribute on class is not set and the property
// does not have the NullableAttribute, then the proeprty is non nullable
if (propertyNullableContext == null)
{
return true;
}
// nullableContext == 0 means context is null oblivious (Ex. Pre C#8)
// nullableContext == 1 means not nullable
// nullableContext == 2 means nullable
switch (propertyNullableContext)
{
case NonNullableContextValue:
return false;
case NullableContextValue:
return true;
default:
throw new Exception("My error message");
}
}
以下是有关可为 null 的上下文值的一些信息:https://www.postsharp.net/blog/post/PostSharp-internals-handling-csharp-8-nullable-reference-types
我有一个使用 C#-8 并启用了可空类型的 .NET Core 项目。
我有以下class
public class MyClass
{
public int? NullableInt { get; private set; }
public string? NullableString { get; private set; }
public string NonNullableString { get; private set; }
public MySubClass? MyNullableSubClass { get; private set; }
}
我需要能够遍历所有属性 class 并确定哪些属性可为空。
所以我的代码看起来像这样
public IEnumerable<string> GetNullableProperties(Type type)
{
var nullableProperties = new List<string>();
foreach (var property in type.GetProperties())
{
var isNullable = false;
if (property.PropertyType.IsValueType)
{
isNullable = Nullable.GetUnderlyingType(property.PropertyType) != null;
} else {
var nullableAttribute = property.PropertyType.CustomAttributes
.FirstOrDefault(a => a.AttributeType.Name == "NullableAttribute");
isNullable = nullableAttribute != null;
}
if (isNullable)
{
nullableProperties.Add(property.propertyType.Name)
}
}
return nullableProperties;
}
正在将 MyClass
的类型传递给此方法 returns ["NullableInt", "NullableString", "NonNullableString", "MyNullableSubClass"]
.
然而,预期的 return 值为 ["NullableInt", "NullableString", "MyNullableSubClass"]
。
NonNullableString
属性 之所以被确定为可空,是因为它具有 Nullable 属性。
我的理解是判断一个引用类型是否为nullable时,需要看它是否具有Nullable属性。但是,字符串类型似乎并非如此。似乎所有字符串都定义了可为空的属性。有没有办法找出 string
是否可以为空(即使用可空运算符 ?
定义)。
您需要检查 属性 本身的自定义属性,而不是 属性 的类型。
public IEnumerable<string> GetNullableProperties(Type type)
{
var nullableProperties = new List<string>();
foreach (var property in type.GetProperties())
{
var isNullable = false;
if (property.PropertyType.IsValueType)
{
isNullable = Nullable.GetUnderlyingType(property.PropertyType) != null;
}
else
{
var nullableAttribute = property.CustomAttributes
.FirstOrDefault(a => a.AttributeType.Name == "NullableAttribute");
isNullable = nullableAttribute == null;
}
if (isNullable)
{
nullableProperties.Add(property.Name);
}
}
return nullableProperties;
}
此外,如果 属性 可以为 null,则此属性未定义。如果 属性 不可为空,则此属性存在。
我找到了完整的解决方案。我们需要检查 属性 和 class.
上的自定义属性
...
private const byte NonNullableContextValue = 1;
private const byte NullableContextValue = 2;
public IEnumerable<string> GetNullableProperties(Type type)
{
foreach (var property in type.GetProperties())
{
var isNullable = property.PropertyType.IsValueType
? Nullable.GetUnderlyingType(property.PropertyType) != null;
: IsReferenceTypePropertyNullable(property);
if (isNullable)
{
nullableProperties.Add(property.propertyType.Name)
}
}
return nullableProperties;
}
private function bool IsReferenceTypePropertyNullable(PropertyInfo property)
{
var classNullableContextAttribute = property.DeclaringType.CustomerProperties
.FirstOrDefault(c => c.AttributeType.Name == "NullableContextAttribute")
var classNullableContext = classNullableContextAttribute
?.ConstructorArguments
.First(ca => ca.ArgumentType.Name == "Byte")
.Value;
// EDIT: This logic is not correct for nullable generic types
var propertyNullableContext = property.CustomAttributes
.FirstOrDefault(c => c.AttributeType.Name == "NullableAttribute")
?.ConstructorArguments
.First(ca => ca.ArgumentType.Name == "Byte")
.Value;
// If the property does not have the nullable attribute then it's
// nullability is determined by the declaring class
propertyNullableContext ??= classNullableContext;
// If NullableContextAttribute on class is not set and the property
// does not have the NullableAttribute, then the proeprty is non nullable
if (propertyNullableContext == null)
{
return true;
}
// nullableContext == 0 means context is null oblivious (Ex. Pre C#8)
// nullableContext == 1 means not nullable
// nullableContext == 2 means nullable
switch (propertyNullableContext)
{
case NonNullableContextValue:
return false;
case NullableContextValue:
return true;
default:
throw new Exception("My error message");
}
}
以下是有关可为 null 的上下文值的一些信息:https://www.postsharp.net/blog/post/PostSharp-internals-handling-csharp-8-nullable-reference-types