为什么我没有收到有关在C#8中使用结构的类成员可能会取消引用null的警告? [英] Why don't I get a warning about possible dereference of a null in C# 8 with a class member of a struct?

查看:89
本文介绍了为什么我没有收到有关在C#8中使用结构的类成员可能会取消引用null的警告?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在具有可引用类型的C#8项目中启用后,我有以下代码,我认为应该向我发出有关可能的空取消引用的警告,但不会:

In a C# 8 project with nullable reference types enabled, I have the following code which I think should give me a warning about a possible null dereference, but doesn't:

public class ExampleClassMember
{
    public int Value { get; }
}

public struct ExampleStruct
{
    public ExampleClassMember Member { get; }
}

public class Program
{
    public static void Main(string[] args)
    {
        var instance = new ExampleStruct();
        Console.WriteLine(instance.Member.Value);  // expected warning here about possible null dereference
    }
}

何时 instance 使用默认构造函数 instance.Member 设置为默认值 ExampleClassMember ,即 null 。因此, instance.Member.Value 将在运行时抛出 NullReferenceException 。据我了解C#8的可空性检测,我应该收到关于这种可能性的编译器警告,但我没有。为什么?

When instance is initialized with the default constructor, instance.Member is set to the default value of ExampleClassMember, which is null. Thus, instance.Member.Value will throw a NullReferenceException at runtime. As I understand C# 8's nullability detection, I should get a compiler warning about this possibility, but I don't; why is that?

推荐答案

请注意,调用 Console.WriteLine()。引用类型属性不是可为空的类型,因此编译器无需警告它可能为空。

Note that there is no reason for there to be a warning on the call to Console.WriteLine(). The reference type property is not a nullable type, and so there's no need for the compiler to warn that it might be null.

您可能会认为编译器应发出警告结构本身中的引用。在我看来,这是合理的。但是,事实并非如此。这似乎是一个漏洞,是由值类型的默认初始化引起的,即,必须始终有一个默认(无参数)构造函数,该构造函数总是始终将所有字段清零(对于引用类型字段为空,对于数字类型为零,等等)。 )。

You might argue that the compiler should warn about the reference in the struct itself. That would seem reasonable to me. But, it doesn't. This seems to be a loophole, caused by the default initialization for value types, i.e. there must always be a default (parameterless) constructor, which always just zeroes out all the fields (nulls for reference type fields, zeroes for numeric types, etc.).

我称它为漏洞,因为从理论上讲,不可为空的引用值实际上应该始终为非!咄。 :)

I call it a loophole, because in theory non-nullable reference values should in fact always be non-null! Duh. :)

此博客文章似乎已解决了此漏洞:在C#中引入可空引用类型

This loophole appears to be addressed in this blog article: Introducing Nullable Reference Types in C#


避免空值
到目前为止,警告是关于防止可空引用中的空值被取消引用。硬币的另一面是要避免在不可为空的引用中完全没有null。

Avoiding nulls So far, the warnings were about protecting nulls in nullable references from being dereferenced. The other side of the coin is to avoid having nulls at all in the nonnullable references.

有几种方法可以使null值存在,其中大多数值得警告,而其中一些会引起另一个警告之海,最好避免:

There are a couple of ways null values can come into existence, and most of them are worth warning about, whereas a couple of them would cause another "sea of warnings" that is better to avoid:


  • 使用具有非空引用类型字段的结构的默认构造函数。这是一个偷偷摸摸的工作,因为默认构造函数(将结构归零)甚至可以在很多地方隐式使用。 最好不要发出警告 [强调我的-PD] ,否则许多现有的结构类型将变得无用。

  • Using the default constructor of a struct that has a field of nonnullable reference type. This one is sneaky, since the default constructor (which zeroes out the struct) can even be implicitly used in many places. Probably better not to warn [emphasis mine - PD], or else many existing struct types would be rendered useless.

换句话说,是的,这是一个漏洞,但不是漏洞。语言设计者已经意识到了这一点,但是选择了避免出现这种情况,因为考虑到 struct 初始化工作的方式,否则这样做是不切实际的。

In other words, yes this is a loophole, but no it's not a bug. The language designers are aware of it, but have chosen to leave this scenario out of the warnings, because to do otherwise would be impractical given the way struct initialization works.

请注意,这也符合该功能背后的广泛理念。在同一篇文章中:

Note that this is also in keeping with the broader philosophy behind the feature. From the same article:


所以我们希望它抱怨您现有的代码。但并非令人讨厌。我们将尝试达到这种平衡:

So we want it to complain about your existing code. But not obnoxiously. Here’s how we are going to try to strike that balance:


  1. 即使您对所有警告做出了反应并消除了所有警告,也无法保证空安全性 [强调防雷-PD]

  1. There is no guaranteed null safety [emphasis mine - PD], even if you react to and eliminate all the warnings. There are many holes in the analysis by necessity, and also some by choice.

最后一点:有时警告是正确的操作,但会始终在现有代码上触发,即使实际上是以空安全方式编写的也是如此。在这种情况下,我们会偏向于方便而不是正确。我们不能在现有代码上产生警告之海:太多的人只会关闭警告而从中受益匪浅。

To that last point: Sometimes a warning is the "correct" thing to do, but would fire all the time on existing code, even when it is actually written in a null safe way. In such cases we will err on the side of convenience, not correctness. We cannot be yielding a "sea of warnings" on existing code: too many people would just turn the warnings back off and never benefit from it.

还请注意,名义上不可为空的引用类型(例如 string [] )的数组也存在相同的问题。创建数组时,所有参考值均为 null ,但这是合法的,不会生成任何警告。

Also note that this same issue exists with arrays of nominally non-nullable reference types (e.g. string[]). When you create the array, all of the reference values are null, and yet this is legal and won't generate any warnings.



这么多的东西可以解释事物为什么如此。然后问题变成了,该怎么办?这更加主观,我认为答案是对还是错。那就是…


So much for explaining why things are the way the are. Then the question becomes, what to do about it? That's a lot more subjective, and I don't think there's a right or wrong answer. That said…

我个人会视情况处理我的 struct 类型。对于 intent 实际上是可为空的引用类型的那些对象,我将应用批注。否则,我不会。

I personally would treat my struct types on a case-by-case basis. For those where the intent is actually a nullable reference type, I would apply the ? annotation. Otherwise, I would not.

从技术上讲,结构中的每个参考值都应为可为空,即在类型名称中包含可为空的注释。但是,与许多类似功能(例如C#中的async / await或C ++中的 const )一样,这具有感染性的方面,因为您要么需要重写该特性,要么

Technically, every single reference value in a struct should be "nullable", i.e. include the ? nullable annotation with the type name. But as with many similar features (like async/await in C# or const in C++), this has an "infectious" aspect, in that you'll either need to override that annotation later (with the ! annotation), or include an explicit null check, or only ever assign that value to another nullable reference type variable.

对我来说,这违背了启用可空引用类型的许多目的。由于这样的 struct 类型的成员无论如何在某些时候都需要特殊情况处理,并且是唯一真正安全地处理 的方法能够使用非空引用类型的方法是在使用 struct 的任何地方放置空检查,我认为当代码初始化<$ c时接受这种选择是一个合理的实现选择$ c> struct ,这是代码的责任,正确执行此操作并确保实际上将非空引用类型成员初始化为非空值。

To me, this defeats a lot of the purpose of enabling nullable reference types. Since such members of struct types will require special-case handling at some point anyway, and since the only way to truly safely handle it while still being able to use non-nullable reference types is to put null checks everywhere you use the struct, I feel that it's a reasonable implementation choice to accept that when code initializes the struct, it is that code's responsibility to do so correctly and make sure the non-nullable reference type member is in fact initialized to a non-null value.

这可以通过提供一种官方的初始化方式来辅助,例如非默认构造函数(即带有参数的构造函数)或工厂方法。仍然总是有使用默认构造函数的风险,或者根本没有使用任何构造函数的风险(如在数组分配中一样),但是通过提供一种方便的方法来正确初始化 struct

This can be aided by providing an "official" means of initialization, such as a non-default constructor (i.e. one with parameters) or factory method. There will still always be the risk of using the default constructor, or no constructor at all (as in array allocations), but by providing a convenient means to initialize the struct correctly, this will encourage code that uses it to avoid null references in non-nullable variables.

也就是说,如果您想要的是关于可空引用类型的100%安全,那么显然,针对该特定目标的正确方法是始终使用注释结构中的每个引用类型成员。这意味着每个字段和每个自动实现的属性,以及直接返回此类值或此类值的乘积的任何方法或属性获取器。然后,在将这些值复制到不可为空的变量的每个点上,使用方代码将需要包括空检查或允许空的运算符。

That said, if what you want is 100% safety with respect to nullable reference types, then clearly the correct approach for that particular goal is to always annotate every reference type member in a struct with ?. This means every field and every auto-implemented property, along with any method or property getter that directly returns such values or the product of such values. Then the consuming code will need to include null checks or the null-forgiving operator at every point where such values are copied into non-nullable variables.

这篇关于为什么我没有收到有关在C#8中使用结构的类成员可能会取消引用null的警告?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆