I have successfully read and used the NullableAttribute/NullableContextAttribute (based on https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md and too much trial and errors) for properties, fields and method parameters.
However, I'm failing to analyze the nullabilities of a generic parameter. My goal is to obtain the nullability information of the IA type parameter (the tuple):
interface ICommand<T> { }
interface IA : ICommand<(string,List<string?>?)> { }
However, the Nullable and NullableContext attributes are clearly not enough. The code below dumps these attributes on the type tree:
[Test]
public void type_in_generic_definition()
{
var b = new StringBuilder();
Dump( typeof( IA ), b, Environment.NewLine );
Dump( typeof( IA ).GetInterfaces()[0], b, Environment.NewLine );
Console.WriteLine( b );
}
void Dump( Type type, StringBuilder w, string newline )
{
newline = newline + " ";
w.Append( type.Name ).Append( newline );
var p = GetNullableProfile( type );
w.Append( $"Nullable: {(p != null ? string.Join( null, p.Select( v => v.ToString() ) ) : "null")}" ).Append( newline );
var c = GetNullableContextValue( type );
w.Append( $"NullableContext: {(c != null ? c.ToString() : "null")}" ).Append( newline );
var d = GetNullableContextValueFromDeclaringType( type );
w.Append( $"NullableContext (DeclaringType): {(d != null ? d.ToString() : "null")}" ).Append( newline );
if( type.IsGenericType )
{
foreach( var sub in type.GetGenericArguments() )
{
Dump( sub, w, newline );
}
}
newline = newline.Substring( 0, newline.Length - 2 );
w.Append( newline );
}
byte[]? GetNullableProfile( Type t )
{
var a = t.CustomAttributes.FirstOrDefault( a => a.AttributeType.Name == "NullableAttribute" && a.AttributeType.Namespace == "System.Runtime.CompilerServices" );
if( a == null ) return null;
object? data = a.ConstructorArguments[0].Value;
Debug.Assert( data != null );
if( data is byte b ) return new[] { b };
return ((IEnumerable<CustomAttributeTypedArgument>)data).Select( a => (byte)a.Value! ).ToArray();
}
byte? GetNullableContextValue( Type t )
{
var a = t.CustomAttributes.FirstOrDefault( a => a.AttributeType.Name == "NullableContextAttribute" && a.AttributeType.Namespace == "System.Runtime.CompilerServices" );
return a == null
? null
: (byte)a.ConstructorArguments[0].Value!;
}
byte? GetNullableContextValueFromDeclaringType( Type t )
{
var parent = t.DeclaringType;
while( parent != null )
{
var found = GetNullableContextValue( parent );
if( found.HasValue ) return found;
parent = parent.DeclaringType;
}
return null;
}
This gives me this description that doesn't make any sense (at least to me) regarding the actual nullabilities of (string,List<string?>?)
(I've commented the lines after the //).
IA
Nullable: null
NullableContext: null
NullableContext (DeclaringType): 1 // I'm working in a NRT aware context where
// reference types are by default not annotated
// (not null). Ok.
ICommand`1
Nullable: null
NullableContext: 2 // Q0: On what scope does this apply?
NullableContext (DeclaringType): 1 // Ok. It's my context.
ValueTuple`2
Nullable: null // Ouch! I was expecting a "122" here :(
NullableContext: null
NullableContext (DeclaringType): null
String
Nullable: 0 // Q1: Explicit oblivious here. Was expecting 1!
NullableContext: 1 // Q2: Scope? Is it the 1 I'm after?
NullableContext (DeclaringType): null
List`1
Nullable: 0 // The same as the string!
NullableContext: 1 // But the list IS nullable!
NullableContext (DeclaringType): null
String
Nullable: 0 // The inner string is nullable :(
NullableContext: 1
NullableContext (DeclaringType): null
Where should I look for this? What did I miss?
Aucun commentaire:
Enregistrer un commentaire