使用反射确定C#中引用类型的可空性

使用反射确定C#中引用类型的可空性,c#,.net,.net-core,c#-8.0,nullable-reference-types,C#,.net,.net Core,C# 8.0,Nullable Reference Types,我启用了使用C#-8和可空类型的.NET核心项目 我有以下课程 公共类MyClass { public int?NullableInt{get;private set;} 公共字符串?NullableString{get;private set;} 公共字符串非空字符串{get;private set;} 公共MySubClass?MyNullableSubClass{get;private set;} } 我需要能够遍历类的所有属性,并确定哪些属性可以为null 我的代码是这样的 publi

我启用了使用C#-8和可空类型的.NET核心项目

我有以下课程

公共类MyClass
{
public int?NullableInt{get;private set;}
公共字符串?NullableString{get;private set;}
公共字符串非空字符串{get;private set;}
公共MySubClass?MyNullableSubClass{get;private set;}
}
我需要能够遍历类的所有属性,并确定哪些属性可以为null

我的代码是这样的

public IEnumerable GetNullableProperties(类型)
{
var nullableProperties=新列表();
foreach(类型.GetProperties()中的var属性)
{
var isNullable=false;
if(property.PropertyType.IsValueType)
{
isNullable=Nullable.GetUnderlyingType(property.PropertyType)!=null;
}否则{
var nullableAttribute=property.PropertyType.CustomAttributes
.FirstOrDefault(a=>a.AttributeType.Name==“NullableAttribute”);
isNullable=nullableAttribute!=null;
}
如果(可为空)
{
nullableProperties.Add(property.propertyType.Name)
}
}
返回可空属性;
}
MyClass
的类型传递给此方法将返回
[“NullableInt”、“NullableString”、“NonNullableString”、“MyNullableSubClass”]

但是,预期的返回值是
[“NullableInt”、“NullableString”、“MyNullableSubClass”]

NonNullableString
属性被确定为可空的原因是它具有可空属性


我的理解是,在确定引用类型是否可为Null时,需要检查它是否具有nullable属性。但是,字符串类型似乎不是这样。似乎所有字符串都定义了nullable属性。是否有办法确定
字符串
是否可为空(即,使用可为空运算符
定义?
)。

您需要检查属性本身中的自定义属性,而不是属性类型中的自定义属性

    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;
    }
public IEnumerable GetNullableProperties(类型)
{
var nullableProperties=新列表();
foreach(类型.GetProperties()中的var属性)
{
var isNullable=false;
if(property.PropertyType.IsValueType)
{
isNullable=Nullable.GetUnderlyingType(property.PropertyType)!=null;
}
其他的
{
var nullableAttribute=property.CustomAttributes
.FirstOrDefault(a=>a.AttributeType.Name==“NullableAttribute”);
isNullable=nullableAttribute==null;
}
如果(可为空)
{
nullableProperties.Add(property.Name);
}
}
返回可空属性;
}

此外,如果属性可为null,则不定义此属性。如果属性不可为null,则该属性存在。

我找到了完整的解决方案。我们需要检查属性和类上的自定义属性


...


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");
    }
}

...
私有常量字节NonNullableContextValue=1;
私有常量字节NullableContextValue=2;
公共IEnumerable GetNullableProperties(类型)
{
foreach(类型.GetProperties()中的var属性)
{
var isNullable=property.PropertyType.IsValueType
?Nullable.GetUnderlineType(property.PropertyType)!=null;
:IsReferenceTypePropertyNullable(属性);
如果(可为空)
{
nullableProperties.Add(property.propertyType.Name)
}
}
返回可空属性;
}
私有函数bool是ReferenceTypePropertyNullable(PropertyInfo属性)
{
var classNullableContextAttribute=property.DeclaringType.CustomerProperties
.FirstOrDefault(c=>c.AttributeType.Name==“NullableContextAttribute”)
var classNullableContext=classNullableContextAttribute
?施工论证
.First(ca=>ca.ArgumentType.Name==“字节”)
价值
//编辑:对于可为空的泛型类型,此逻辑不正确
var propertyNullableContext=property.CustomAttributes
.FirstOrDefault(c=>c.AttributeType.Name==“NullableAttribute”)
?施工论证
.First(ca=>ca.ArgumentType.Name==“字节”)
价值
//如果属性没有nullable属性,则它是
//可空性由声明类决定
propertyNullableContext???=classNullableContext;
//如果未在类上设置NullableContextAttribute,则
//如果没有NullableAttribute,则proeprty不可为Null
if(propertyNullableContext==null)
{
返回true;
}
//nullableContext==0表示上下文不受null影响(例如C#8之前)
//nullableContext==1表示不可为Null
//nullableContext==2表示可以为Null
开关(propertyNullableContext)
{
大小写非NullableContextValue:
返回false;
大小写NullableContextValue:
返回true;
违约:
抛出新异常(“我的错误消息”);
}
}

以下是关于可为空的上下文值的一些信息:

谢谢Viktor。我想最后一句应该颠倒过来。此外,这个问题还有更多的问题。我还需要查看类上的自定义属性。我在下面发布了一个完整的解决方案。我在这里也有一篇博客文章,你可能会发现它很有用:-注意,对于泛型类型来说,它变得相当复杂。可能这篇文章与下一篇文章是重复的:这比这里或链接问题封面下的任何答案都要复杂得多。名为的库有一个似乎万无一失的实现。