【发布时间】:2020-06-16 15:39:41
【问题描述】:
在这个人为的 C# 8 示例中:
#nullable enable
class Fred<T>
{
T Value; // If T is a nullable type, Value can be null.
public Fred() { }
public void SetValue(T value) { Value = value; }
public T GetValue() { return Value; }
public string Describe() { return Value.ToString() ?? "oops"; }
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George g = fredGeorge.GetValue();
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
我有三个设计目标:
- 如果我为 Fred 编写任何盲目假设“Value”永远不会为 null 的方法,编译器会警告我
- 如果我在 Fred 之外(比如在 George 中)编写任何盲目假设“GetValue”永远不会返回 null 的方法,编译器会警告我。
- 没有编译器警告(如果我编写的代码不会盲目假设值不会为空)
所以这个第一个版本还不错,我在 Fred 中收到一条警告,说 Describe() 可能正在取消引用空引用(满足目标 #1),但我也收到一条警告,指出在 Fred 的构造函数中 Value 未初始化(违反目标#3) 并且 George 编译时没有任何警告(违反目标 #2)。如果我进行此更改:
public Fred() { Value = default; }
George 仍然在没有警告的情况下进行编译(违反目标 #2),我在 Fred 的构造函数中收到关于“可能的空引用分配”的不同警告(违反目标 #3)。
我可以通过使用 null-forgiving 运算符摆脱可能的 null 引用分配:
public Fred() { Value = default!; }
现在 Fred 只有正确的警告(可能在 Describe() 中取消引用),但 George 也编译时没有警告(违反目标 #2)。
如果我试图表明“值”可以为空:
T? Value;
我收到一个编译器错误,提示“必须知道可以为空的类型参数是值类型或不可为空的引用类型”,所以这不好。
如果我回到
T Value;
并添加“MaybeNull”属性:
[return: MaybeNull]
public T GetValue() { return Value; }
我收到两个警告 - 一个在 Fred.Describe() 中警告可能为 null 取消引用(正确),另一个在 George 中警告 fredGeorge.GetValue() 可能为 null(正确)。没有关于 fredFloat.GetValue() 为空(正确)的警告。
所以在添加代码以期望空引用之后,我最终得到的是:
class Fred<T>
{
T Value;
public Fred()
{
Value = default!;
}
public void SetValue(T value)
{
Value = value;
}
[return: MaybeNull]
public T GetValue()
{
return Value;
}
public string Describe()
{
return (Value == null) ? "null" : (Value.ToString() ?? "ToString is null");
}
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George? g = fredGeorge.GetValue();
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
这是该功能的正确模式吗?
【问题讨论】:
-
为什么使用
Value字段和GetValue和SetValue两种方法而不是常规属性?此外,您的代码没有考虑到null值可以传递给方法SetValue。如果您在George类中编写代码fredGeorge.SetValue(null),您将收到警告:Cannot convert null literal to non-nullable reference type.。 -
我以这种方式编写 GetValue() 和 SetValue() 只是为了探索和理解 C# 可空性特性。是的,在真正的应用程序中,我只会使用常规属性。关于 SetValue(null),你说得很对 - 你知道我需要做什么来解决这个问题吗?
标签: c# nullable c#-8.0 nullable-reference-types