如何告知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
相关产品推荐
相关产品推荐

