.NET不可为null的引用类型和out参数



我修改了csproj文件,以便在C#8:中启用空引用类型

<Nullable>enable</Nullable>

给定以下代码:

private static void Method()
{
var dictionary = new Dictionary<string, string>();
string value = string.Empty;
dictionary.TryGetValue("Key", out value);
}

带有TryGetValue()的线路发出警告:

CS8600:将null文本或可能的null值转换为不可为null的类型。

我不明白为什么。TryGetValue()的签名为:

public bool TryGetValue(string key, [MaybeNullWhen(false)] out string value);

代码示例只有不可为null的引用。为什么会出现此错误?

如果在字典中找不到"Key",则会将值null分配给value变量。但是,您已经将value声明为string,这意味着它不应该包含null。因此编译器会向您发出警告。

您最初已将string.Empty分配给value这一事实并不重要——它总是会被TryGetValue覆盖(您应该会收到另一条警告(。

您应该将value声明为string?,以指示其值可能是null

请注意,编译器非常聪明。如果你写:

if (!dictionary.TryGetValue("Key", out string? value))
{
value = string.Empty;
}

那么编译器知道value不可能是null,如果您尝试并调用它上的方法,它不会抱怨。

canton7的答案是正确的(+1(
这不是一个解释,而是一个解决方法:
您可以向Dictionary<TVey, TValue>添加一个扩展方法,如下所示:

public static bool TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue @default, out TValue @value) where TKey : notnull
{
var result = dictionary.TryGetValue(key, out var val);
@value = result ? val : @default;
return result;
}

然后你可以这样使用它:

private static void Method()
{
var dictionary = new Dictionary<string, string>();
/// populate dictionary here...
dictionary.TryGetValue("Key", string.Empty, out var value);
}

这将使您能够将value保留为不可为null的字符串。

来自C#编译器解释的空状态静态分析的文档属性。

在启用了null的上下文中,编译器对代码执行静态分析,以确定所有引用类型变量的null状态:

  • not null:静态分析确定变量具有非null值
  • maybe null:静态分析无法确定变量是否分配了非null值

静态分析器认为变量可以是:

  • 可以为null
  • 不可为null
  • 可能为null

当用属性MaybeNull修饰不可为null的变量时,静态分析器认为该变量可能为null。

[return: MaybeNull]
static string Find(string key)
{
return key == "" ? null : key;
}
string value1 = Find("key"); // Warning CS8600 Converting null literal or possible null value to non-nullable type.
string? value2 = Find("key"); // No warning
var value3 = Find("key"); // The inferred type is 'string?'

MaybeNullWhen属性类似,但静态分析器可以根据方法的结果处理检查。

static bool TryGetValue(string key, [MaybeNullWhen(false)] out string value)
{
if(key == "")
{
value = null;
return false;
}
value = "Foo";
return true;
}
string notnullable;
string value1;
if (TryGetValue("Key", out value1)) // Warning CS8600 Converting null literal or possible null value to non-nullable type.
notnullable = value1;
else
notnullable = value1; // Warning CS8600 Converting null literal or possible null value to non-nullable type.
string? value2;
if (TryGetValue("Key", out value2))
notnullable = value2;
else
notnullable = value2; // Warning CS8600 Converting null literal or possible null value to non-nullable type.

我同意,这在这些例子中毫无意义。但是使用泛型方法,您可以指定一个不可为null的类型,而该方法可以返回/设置null:

[return: MaybeNull]
static T Find<T>(string key);
static bool TryGetValue<T>(string key, [MaybeNullWhen(false)] out T value)

最新更新