7

I have the following method which takes a Type as a parameter and checks if it is IEnumerable<T>. If it is, it returns true and sets the type of T in an out variable, otherwise it returns false.

private static bool TryGetEnumeratedType(Type type, [NotNullWhen(true)] out Type? enumeratedType)
{
    enumeratedType = null;
    bool isEnumerable = type.GetInterfaces().Any(i => i.GetTypeInfo() == typeof(IEnumerable));

    if (!isEnumerable)
    {
        return false;
    }

    Type? genericType = type.GetGenericArguments().FirstOrDefault();

    if (genericType is null)
    {
        return false;
    }

    enumeratedType = genericType;

    return true;
}

If I call the method this way, there is no issue:

Type fieldType;

if (TryGetEnumeratedType(inType, out Type? type))
{
    fieldType = type;
}
else
{
    fieldType = inType;
}

But if I assign the result to a variable and check against the variable, I get a compiler warning when assigning fieldType = type saying that type may be null.

Type fieldType;
bool isEnumerable = TryGetEnumeratedType(inType, out Type? type);

if (isEnumerable)
{
    fieldType = type;
}
else
{
    fieldType = inType;
}

Is this a bug, or am I missing something?

2 Answers 2

2

I believe that has nothing to do with NotNullWhen. The compiler just doesn't do that kind of work - it doesn't go into deep checks of "where did that variable come from". Doing that would probably be 1) hard to implement and 2) you wouldn't know where to stop: how high up the call stack should compiler go to ensure there is no null assigned to your value? That would probably be very resource-intensive. This answer discusses it a bit.

Consider these two methods, for example:

public int GetInt() //no warning
{
    if (true)
        return 1; 
}

public int GetAnotherInt() //CS0161: not all code paths return a value
{
    var @true = true;
    if (@true)
        return 1; 
}
Sign up to request clarification or add additional context in comments.

Comments

0

I tested out your code myself and in .NET 8.0 it still is an issue. It seems that the validation of these static analysis attributes such as [NotNullWhen] needs to be fixed for method parameters.

A search for these static analysis attributes reveals still a multitude of issues and it looks like they do not handle every case. This issue reported in is similar to the one you show here: https://github.com/dotnet/roslyn-analyzers/issues/6990

After all, the Roslyn team tries to create a mechanism for automated null checks, but there are many edge cases and issues takes years to resolve by looking through when open issues on this topic was reported.

For me, it seems that the fix for you for now is to use the null forgiving operator inside the if clause on the RHS.

if (isEnumerable)
{
    fieldType = type!;
}

Then you can head over to Github Issues page for Roslyn here and file an issue : https://github.com/dotnet/roslyn-analyzers/issues

I tried some of the other attributes such as [MaybeNullWhen] attribute looking at the documentation, but could not find a working alternative.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis#conditional-post-conditions-notnullwhen-maybenullwhen-and-notnullifnotnull

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.