Skip to content

NullabilityInfoContext reports null/notnull for unspecified generic parameters #63660

Closed
@madelson

Description

@madelson

Description

For a generic parameter T on a class with no type constraints indicating nullability, Unknown would seem most appropriate nullability state to return from NullabilityInfoContext because there are no constraints on that type with respect to nullability.

However, the value returned seems to depend on whether the type is declared in a nullable or non-nullable context

Reproduction Steps

public static void Main()
    {
        var context = new NullabilityInfoContext();

        var fooInfo = context.Create(typeof(Foo<string?>).GetConstructors().Single().GetParameters().Single());
        Console.WriteLine(fooInfo.WriteState); // NotNull (I would expect Unknown; NotNull is clearly wrong here given that I can make Foo<string?>)

        var barInfo = context.Create(typeof(Bar<string?>).GetConstructors().Single().GetParameters().Single());
        Console.WriteLine(barInfo.WriteState); // Unknown
    }

#nullable disable
    private class Foo<T> { public Foo(T t) {  } }
#nullable enable

#nullable disable
    private class Bar<T> { public Bar(T t) { } }
#nullable enable

Expected behavior

Consistently returns Unknown.

Actual behavior

Sometimes returns NotNull.

Regression?

No response

Known Workarounds

No response

Configuration

VS 17.0.2, .NET 6, Windows 10 x64.

I don't have any reason to believe this is configuration specific.

Other information

C#9 allows for both T and T? to be used in generic classes even without any constraint that indicates whether or not T can be null:

class Generic<T> { public void Foo(T a, T? b) { } }

It's unclear what NullabilityInfoContext should return when reflecting over such types. On one hand, both could be either non-nullable or nullable depending on the particular generic argument that ends up getting used, which makes Unknown seem reasonable for both from that perspective.

On the other hand, it seems nice to be able to differentiate between the two in some way. Perhaps it makes sense to add another null state that explicitly represents the case of NotNullIfGenericArgumentIsNonNullableReferenceType?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions