Cela semble fonctionner, au moins sur les types avec lesquels je l'ai testé.
Vous devez transmettre le PropertyInfo
pour la propriété qui vous intéresse, ainsi Type
que celui sur lequel cette propriété est définie ( pas un type dérivé ou parent - ce doit être le type exact):
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;
}
Consultez ce document pour plus de détails.
L'essentiel est que la propriété elle-même peut avoir un [Nullable]
attribut, ou si ce n'est pas le cas, le type peut avoir un [NullableContext]
attribut. Nous cherchons d'abord [Nullable]
, puis si nous ne le trouvons pas, nous recherchons [NullableContext]
le type englobant.
Le compilateur peut incorporer les attributs dans l'assembly, et comme nous pouvons examiner un type d'un assembly différent, nous devons effectuer une charge de réflexion uniquement.
[Nullable]
peut être instancié avec un tableau, si la propriété est générique. Dans ce cas, le premier élément représente la propriété réelle (et les autres éléments représentent des arguments génériques). [NullableContext]
est toujours instancié avec un seul octet.
Une valeur de 2
signifie "nullable". 1
signifie "non annulable" et 0
signifie "inconscient".
[NullableContext(2), Nullable((byte) 0)]
au type (Foo
) - c'est donc ce qu'il faut vérifier, mais j'aurais besoin de creuser davantage pour comprendre les règles d'interprétation!