C# 8.0 introduces nullable reference types. Here\'s a simple class with a nullable property:
public class Foo
{
pu
I wrote a library to do reflection of NRT types - internally it looks at the generated attributes and gives you a simple API:
https://github.com/RicoSuter/Namotion.Reflection
This appears to work, at least on the types I've tested it with.
You need to pass the PropertyInfo
for the property you're interested in.
public static bool IsNullable(PropertyInfo property)
{
if (property.PropertyType.IsValueType)
return Nullable.GetUnderlyingType(property.PropertyType) != null;
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 = property.DeclaringType.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;
}
See this document for details.
The general gist is that either the property itself can have a [Nullable]
attribute on it, or if it doesn't the enclosing type might have [NullableContext]
attribute. We first look for [Nullable]
, then if we don't find it we look for [NullableContext]
on the enclosing type.
The compiler might embed the attributes into the assembly, and since we might be looking at a type from a different assembly, we need to do a reflection-only load.
[Nullable]
might be instantiated with an array, if the property is generic. In this case, the first element represents the actual property (and further elements represent generic arguments). [NullableContext]
is always instantiated with a single byte.
A value of 2
means "nullable". 1
means "not nullable", and 0
means "oblivious".