代码分析无法确定此变量已设置是否有技术原因?

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

提问人:Jez 提问时间:11/11/2023 最后编辑:winscripterJez 更新时间:11/13/2023 访问量:92

问:

我正在对等待的 lambda 中的引用类型变量进行初始赋值,如下所示:

private class TestClass {
    public int TestInt;
}

public async Task TestMethod() {
    TestClass testVar;

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

    var testInt = testVar.TestInt;
}

但是,最后一行给出错误“使用未分配的局部变量'testVar'”。是否有技术原因导致 C# 的代码分析无法确定变量在该点之前已保证已分配?在我第一次使用的地方使用运算符有点烦人。如果我需要首先在等待的 lambda 中分配变量,并且不能方便地给它一个默认赋值(这是一个相当复杂的类),有什么方法可以解决这个问题?!testVar

C# .NET Roslyn Roslyn 代码分析 Microsoft.CodeAnalysis

评论

3赞 JLRishe 11/11/2023
从语法角度来看,您不会在该方法的最后一行之前进行赋值。您正在传递一个 lambda 函数,该函数将其分配给另一个方法,并等待该方法。这就是编译器所知道的全部内容。它不知道 lambda 是否会在您到达访问变量的部分之前运行到完成,或者它是否会运行。但是,如果您确信变量将被赋值,为什么不直接为其赋值初始值呢?testVarnull
0赞 PMF 11/11/2023
@JLRishe我也在考虑这种解决方法,但是当启用可为 null 的引用类型时,它不能很好地工作。你要么必须使 null 为 null,要么分配给它。testVarnull!
0赞 JLRishe 11/11/2023
怎么样var testVar = await Task.Run(() => { TestVar x = new(); return x; });
0赞 Jez 11/11/2023
@JLRishe 这在这种情况下有效,但我可能正在使用一种获取 lambda 的方法,该方法不允许我获取 lambda 的返回值。
0赞 Fildor 11/11/2023
不知道为什么会这样,但如果我在我正在审查的代码中发现它,我建议从根本上重构它。这从 开始,跨越复杂的任务。原因:如果出现错误(假设这是一个简化,到目前为止并不是真实事物中发生的所有事情),这可能是一场调试噩梦。async void

答:

1赞 PMF 11/11/2023 #1

这似乎不是 async/await 问题,而是 lambda 表达式的一般问题。编译器似乎无法遵循 lambda 的路径来检测赋值。

此代码也不编译:

        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
        }

因此,我首先认为编译器通常无法跟踪方法边界上的赋值,但以下代码令人惊讶地没问题:

public void TestMethod()
{
    TestClass testVar;

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

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

因此,这似乎是编译器对 lambda 函数的一些限制,但为什么 - 我说不出来。

2赞 Jez 11/12/2023 #2

基本上,这样做的原因是 C# 流分析无法知道传入的 lambda(或任何等待的获取 lambda 的方法)将在完成时确定执行,因此它不会假设它会执行。出于这个原因,我需要初始化变量,以断言在我访问它时它将是非空的,以摆脱警告/错误。Task.RunawaitTestClass testVar = null!;