Is there a technical reason why code analysis can't figure out that this variable has been set?

119 views Asked by At

I'm making an initial assignment to my reference-type variable inside an awaited lambda, like so:

private class TestClass {
    public int TestInt;
}

public async Task TestMethod() {
    TestClass testVar;

    await Task.Run(() => {
        testVar = new();
    });

    var testInt = testVar.TestInt;
}

However, the last line gives the error "Use of unassigned local variable 'testVar'". Is there a technical reason why C#'s code analysis can't figure out that the variable is guaranteed to be assigned by that point? It's kind of annoying to have the use the ! operator wherever I first use testVar. Is there any way to work round this if I need to first assign the variable inside an awaited lambda, and can't conveniently give it a default assignment (it's quite a complex class)?

2

There are 2 answers

0
PMF On

It appears this is not an async/await issue but a general issue with lambda expressions. The compiler doesn't seem to be able to follow the path of the lambda to detect the assignment.

This code also doesn't compile:

        private class TestClass
        {
            public int TestInt;
        }

        public void TestMethod()
        {
            TestClass testVar;

            Action a = new Action(() =>
            {
                testVar = new();
            });
            a.Invoke();

            var testInt = testVar.TestInt; // Same here: "testVar" is not initialized
        }

So I first thought the compiler was generally unable to track assignments over method boundaries, but the following code surprisingly is ok:

public void TestMethod()
{
    TestClass testVar;

    void Foo()
    {
        testVar = new();
    }

    Foo();
    var testInt = testVar.TestInt; // No more complaints!
}

So this appears to be some limitation of the compiler regarding lambda functions, but why - I cannot tell.

0
Jez On

Basically, the reason for this is that the C# flow analysis has no way of knowing that the lambda passed into Task.Run (or any awaited method taking a lambda) will be executed for sure by the time the await finishes, so it doesn't assume that it will be. For this reason, I need to initialize the variable with TestClass testVar = null!; to assert that it will be non-null by the time I access it, to get rid of the warning/error.