Questo sembra funzionare, almeno sui tipi con cui l'ho provato.
Devi passare il PropertyInfo
per la proprietà che ti interessa, e anche il motivo per Type
cui quella proprietà è definita ( non un tipo derivato o genitore - deve essere il tipo esatto):
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;
}
Vedi questo documento per i dettagli.
L'essenza generale è che o la proprietà stessa può avere un [Nullable]
attributo su di essa, o in caso contrario il tipo racchiuso potrebbe avere un [NullableContext]
attributo. Prima cerchiamo [Nullable]
, quindi se non lo troviamo cerchiamo [NullableContext]
nel tipo allegato.
Il compilatore potrebbe incorporare gli attributi nell'assembly e poiché potremmo guardare un tipo da un assembly diverso, dobbiamo eseguire un caricamento di solo riflessione.
[Nullable]
potrebbe essere istanziato con un array, se la proprietà è generica. In questo caso, il primo elemento rappresenta la proprietà effettiva (e ulteriori elementi rappresentano argomenti generici). [NullableContext]
viene sempre istanziato con un singolo byte.
Un valore di 2
significa "nullable". 1
significa "non nullable" e 0
significa "ignaro".
[NullableContext(2), Nullable((byte) 0)]
al tipo (Foo
) - quindi questo è cosa cercare, ma dovrei scavare di più per capire le regole su come interpretarlo!