【问题标题】:Does C# 8 annotate nullable properties and parameters?C# 8 是否注释了可为空的属性和参数?
【发布时间】:2018-12-06 20:11:31
【问题描述】:

我很好奇可空引用类型是如何工作的,不是在您自己的代码库中,而是在已经编译的库中。 C# 是否能够知道某个属性或参数是否可以为空,或者检查某些编译器添加的属性是否存在?

【问题讨论】:

  • ms doc 中所写,可空值是类型:Nullable<T>其中 T 是值类型。不需要特殊属性。
  • @H.G.Sandhagen 他说的是可为空的 reference 类型,C# 8 中的新概念和支持,不一样。
  • @HGSandhagen 在未发布的 C# 8 中我们得到了像 public string? myString {get; set;} 这样的新东西>
  • 是的,如果你在引用类型之后使用?,它会添加一个属性。已编译的程序集(即未使用 C# 8 编译的程序集)将不具有这些属性。编译器是否会以不同的方式处理该程序集,或者只是假设所有说它返回引用的东西都返回一个非空值,我不知道,这就是我不发布答案的原因。你可以在这里查看结果:sharplab.io/…
  • @LasseVågsætherKarlsen 感谢您提供Sharplab链接,它回答了问题。

标签: c# c#-8.0 nullable-reference-types


【解决方案1】:

看起来 VS2019 预览版 1 和预览版 2 之间的行为发生了变化,这可能是由于可以更改可为空的上下文的方式。不再有每个程序集或每个类型的属性。当然,它可能会再次改变,

在 VS2019 预览版 2 中,表示可空或不可空信息(参数和返回类型)的成员的每个部分都使用 NullableAttribute 单独归属,如有必要,该 NullableAttribute 包含在程序集本身中。该属性有两个构造函数:

NullableAttribute(byte)
NullableAttribute(byte[])

byte 形式用于该参数/返回类型的可空性的各个方面都相同时。 byte[] 用于由于泛型或数组而导致单个元素的可空性混合。在这两种情况下,1 用于“不可为空”,2 用于“可空”。比如:

public class Test
{
    public string? Foo(string input) { ... }

    public List<string>? Bar() { ... }
}

编译为:

public class Test
{
    [return:Nullable(2)]
    public string Foo([Nullable(1)] string input) { ... }

    [return: Nullable(new byte[] { 1, 2 })]
    public List<string> Bar() { ... }
}

这允许任何检查程序集的代码(无论是使用它作为参考的编译器还是其他工具)都可以了解每个成员的意图。

我在a blog post 中写了更多关于此的内容,但这应该足以了解要点。

【讨论】:

    【解决方案2】:

    是的,如果库是使用 C# 8.0 编译器编译的并启用了可空引用类型,编译器将能够识别哪些值被标记为可空。

    例如,考虑以下代码:

    class C
    {
        string NotNullProperty { get; set; }
        string? NullProperty { get; set; }
    
        void M(string notNullParameter, string? nullParameter) {}
    }
    

    It compiles roughly into:

    [NonNullTypes(true)]
    class C
    {
        string NotNullProperty { get; set; }
    
        [Nullable]
        string NullProperty { get; set; }
    
        void M(string notNullParameter, [Nullable] string nullParameter) { }
    }
    

    注意可以为空的属性和参数标记为[Nullable],整个类都标记为[NonNullTypes(true)],表示开启了可以为空的引用类型功能。

    另一方面,如果代码是在没有该功能的情况下编译的,它将被视为“null-oblivious”。这意味着当您使用该代码时,编译器不会产生与 null 相关的警告。

    【讨论】:

    • 更新:[NonNullTypes] 方法已被放弃,取而代之的是专用语法:#nullable enable
    • @JulienCouvreur 我在这里谈论的是元数据表示和 AFAICT,它仍然使用属性(因为元数据中没有预处理器指令之类的东西)。
    • 糟糕。是的,我们仍然使用 Nullable 属性,但我们不再使用 NonNullTypes 属性(即使在元数据中)。 Nullable 现在存储字节数组而不是布尔值(0、1 和 2 用于三种状态)。编码在github.com/dotnet/roslyn/blob/master/docs/features/… 中描述
    • 我现在写了Nullable 作为答案。虽然我还没有看到编译器在任何情况下都应用 0 ......我想我需要解决一个元素的一部分被遗忘的情况。
    【解决方案3】:

    在 Mads Torgersen(微软 C# 语言项目经理)Take C# 8.0 for a spin 中,他说:

    如果您调用没有可空引用类型功能的代码(可能它在该功能甚至存在之前就已编译),那么我们无法知道该代码的意图是什么:它不区分可空和不可为空的——我们说它是“不为空的”。所以我们给它通过;我们根本不会对此类调用发出警告。

    所以不,他们似乎不会标记它,所以你不会收到任何类型的编译器警告。因此,在使用 C# 8 之前的代码时,您似乎必须做一些研究来确定引用是否可以包含 null,而不是依赖类型系统和编译器来警告您。

    【讨论】:

    • 用 Sharplab 进行测试,似乎说他们返回字符串的方法(即他们没有使用 ?)根本没有得到注释,这让我认为方法在简单地假定旧程序集总是返回引用而不是 null,就像在 C# 8 中专门为此编写的方法一样。
    • 是的,这意味着在使用旧代码时,我们仍然必须格外小心地处理它。比平时更加​​小心,因为我们可能会陷入一种虚假的安全感。
    • 我同意,看看结果如何会很有趣。
    猜你喜欢
    • 2022-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    相关资源
    最近更新 更多