You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何告知Visual Studio成员变量不为null?

解决C# Nullable引用类型的空值警告问题

我定义了如下C#类:

class Operation
{
    private int? opA = null;
    private int? opB = null;
    private Func<int>? opFunc = null;

    public void SetOperandA(int value)
        => opA = value;
    public void SetOperandB(int value)
        => opB = value;

    public void SetAdd()
        => SetOperator(() => (int)opA + (int)opB);
    public void SetSubtract()
        => SetOperator(() => (int)opA - (int)opB);

    public void SetOperator(Func<int> op)
    {
        if (opA is null || opB is null)
            throw new Exception("Both operands must be set first!");
        opFunc = op;
    }

    public int Evaluate()
    {
        if (opFunc is null)
            throw new Exception("Operator must be set first!");
        return opFunc();
    }
}

问题在于,SetAdd()方法中,Visual Studio对SetOperator(() => (int)opA + (int)opB);一行发出警告,提示opA和opB可能为null,但实际上SetOperator()方法已经做了非空检查,不会出现这种情况。

而如下SetMultiply()方法的写法就不会触发警告,因为编译器能推断出检查后opA和opB不为null:

public void SetMultiply()
{
    if (opA is null || opB is null)
        throw new Exception("Both operands must be set first!");

    opFunc = () => (int)opA * (int)opB;
}

我尝试为SetOperator()方法添加[MemberNotNull(nameof(opA))]和[MemberNotNull(nameof(opB))]特性,但没有效果。


解决方案

问题的核心是编译器无法推断出:传递给SetOperator的lambda会在SetOperator内部的非空检查之后才执行(编译器只会检查lambda捕获变量时的空值状态,而非执行时的状态)。以下两种方案可以解决这个问题:

方案一:调整SetOperator签名,解耦运算逻辑与操作数捕获

修改SetOperator,让它接受一个接收两个int参数的委托,在SetOperator内部完成操作数的非空验证和转换,lambda只负责运算逻辑:

class Operation
{
    private int? opA = null;
    private int? opB = null;
    private Func<int>? opFunc = null;

    public void SetOperandA(int value)
        => opA = value;
    public void SetOperandB(int value)
        => opB = value;

    public void SetAdd()
        => SetOperator((a, b) => a + b);
    public void SetSubtract()
        => SetOperator((a, b) => a - b);
    public void SetMultiply()
        => SetOperator((a, b) => a * b);

    public void SetOperator(Func<int, int, int> op)
    {
        if (opA is null || opB is null)
            throw new Exception("Both operands must be set first!");
        opFunc = () => op((int)opA, (int)opB);
    }

    public int Evaluate()
    {
        if (opFunc is null)
            throw new Exception("Operator must be set first!");
        return opFunc();
    }
}

这种方式的优势:

  • lambda不再捕获可能为空的opA/opB,而是接收非空的int参数,彻底消除空值警告
  • 非空检查逻辑仅在SetOperator中维护,避免代码重复
  • 运算逻辑与操作数验证逻辑解耦,代码结构更清晰

方案二:在运算方法内提前完成非空转换

如果不想修改SetOperator的原有签名,可以在SetAdd这类方法内部先将字段转换为非空变量,再传递lambda:

public void SetAdd()
{
    int a = opA ?? throw new Exception("Both operands must be set first!");
    int b = opB ?? throw new Exception("Both operands must be set first!");
    SetOperator(() => a + b);
}

这种方式无需改动核心方法,仅在调用层做处理,缺点是会重复非空检查代码。


内容的提问来源于stack exchange,提问作者user137794

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:35:37