-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
Labels
area-System.ReflectionquestionAnswer questions and provide assistance, not an issue with source code or documentation.Answer questions and provide assistance, not an issue with source code or documentation.
Description
Description
When using the NullabilityInfoContext to fetch NullabilityInfo for a generic property, the ReadState and WriteState are always Nullable, even when the defined generic has the type as NotNull.
Reproduction Steps
using System.Reflection;
#nullable enable
namespace NullabilityInfoWithGenerics
{
internal class Program
{
static void Main(string[] args)
{
PropertyInfo valueProperty = typeof(MyGenericClass<string>).GetProperty("Value")!;
NullabilityInfo NullabilityInfo = new NullabilityInfoContext().Create(valueProperty);
// Expect NotNull
Console.WriteLine(NullabilityInfo.ReadState);
}
}
public record MyGenericClass<T>(T Value);
}
#nullable restoreExpected behavior
The resulting NullabilityInfo's ReadState and WriteState should match the implemented generic type. string == NotNull. string? == Nullable
Actual behavior
When genaric T is a reference type, NullablityInfo has the ReadState Nullable.
Regression?
No response
Known Workarounds
Not using generics works fine (see below). But it's not really a work around.
Configuration
Dotnet 8.0
Nullable enabled
Other information
More Expected/Actual
Type | Expected | Actual
-------------------------------------------------------
MyGenericClass`1<String> | NotNull | Nullable
MyGenericClass`1<String> | Nullable | Nullable
MyGenericClass`1<Int32> | NotNull | NotNull
MyGenericClass`1<Int32?> | Nullable | Nullable
MyGenericClass`1<TestClass> | NotNull | Nullable
MyGenericClass`1<TestClass?> | Nullable | Nullable
MyStringClass | NotNull | NotNull
MyNullableStringClass | Nullable | Nullable
MyIntClass | NotNull | NotNull
MyNullableIntClass | Nullable | Nullable
MyTestClassClass | NotNull | NotNull
MyNullableTestClassClass | Nullable | Nullable
using System.Reflection;
#nullable enable
namespace NullabilityInfoWithGenerics
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("{0, -30} | {1, -10} | {2} ", "Type", "Expected", "Actual");
Console.WriteLine("-------------------------------------------------------");
CheckNullability<MyGenericClass<string>>(NullabilityState.NotNull); // Mismatch
CheckNullability<MyGenericClass<string?>>(NullabilityState.Nullable);
CheckNullability<MyGenericClass<int>>(NullabilityState.NotNull);
CheckNullability<MyGenericClass<int?>>(NullabilityState.Nullable);
CheckNullability<MyGenericClass<TestClass?>>(NullabilityState.NotNull); // Mismatch
CheckNullability<MyGenericClass<TestClass?>>(NullabilityState.Nullable);
CheckNullability<MyStringClass>(NullabilityState.NotNull);
CheckNullability<MyNullableStringClass>(NullabilityState.Nullable);
CheckNullability<MyIntClass>(NullabilityState.NotNull);
CheckNullability<MyNullableIntClass>(NullabilityState.Nullable);
CheckNullability<MyTestClassClass>(NullabilityState.NotNull);
CheckNullability<MyNullableTestClassClass>(NullabilityState.Nullable);
}
static void CheckNullability<T>(NullabilityState expected)
{
PropertyInfo valueProperty = typeof(T).GetProperty("Value")!;
NullabilityInfo NullabilityInfo = new NullabilityInfoContext().Create(valueProperty);
if(expected != NullabilityInfo.ReadState)
{
Console.BackgroundColor = ConsoleColor.Yellow;
Console.ForegroundColor = ConsoleColor.Black;
}
Console.WriteLine(
"{0, -30} | {1, -10} | {2} ",
string.Format("{0}<{1}>", typeof(T).Name, string.Join(',', typeof(T).GetGenericArguments().Select(x => x.Name))),
expected,
NullabilityInfo.ReadState);
if (expected != NullabilityInfo.ReadState)
Console.ResetColor();
}
}
public record MyGenericClass<T>(T Value);
public record MyStringClass(string Value);
public record MyNullableStringClass(string? Value);
public record MyIntClass(int Value);
public record MyNullableIntClass(int? Value);
public record MyTestClassClass(TestClass Value);
public record MyNullableTestClassClass(TestClass? Value);
public record TestClass { }
}
#nullable restoreMetadata
Metadata
Assignees
Labels
area-System.ReflectionquestionAnswer questions and provide assistance, not an issue with source code or documentation.Answer questions and provide assistance, not an issue with source code or documentation.