C# 8.0 introduces nullable reference types. Here\'s a simple class with a nullable property:
public class Foo
{
pu
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)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".