C# 8 中的不可为空的引用类型在运行时可以为空吗?

Posted

技术标签:

【中文标题】C# 8 中的不可为空的引用类型在运行时可以为空吗?【英文标题】:Can a non-nullable reference type in C# 8 be null in runtime? 【发布时间】:2020-04-19 05:05:53 【问题描述】:

在我看来,确实不能保证不可为空的变量永远不会为空。想象一下,我有一个类有一个不可为空的属性:

public class Foo

    public Foo(string test)
    
        Test = test;
    
    public string Test get;set;

现在看起来它现在不能为空。但是如果我们用另一个不使用可为空上下文的库来引用这个类,没有什么能阻止它在那里发送 null。

这是正确的还是有一些运行时检查可以确保这一点?

【问题讨论】:

public void Foo(string test)... 还是public Foo(string test)... 谢谢,我已经修好了。当人类过度依赖 R# 来生成构造函数时,就会发生这种情况:) C# 9 将(可能)添加simplified null validation。 简而言之,“可空引用类型”功能完全被破坏了。 【参考方案1】:

这就是 MS 所说的 (https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/upgrade-to-nullable-references#interfaces-with-external-code):

编译器无法验证对公共 API 的所有调用,即使您的代码是在启用可空注释上下文的情况下编译的。此外,您的库可能会被尚未选择使用可为空引用类型的项目使用。验证公共 API 的输入,即使您已将它们声明为不可空类型。

【讨论】:

【参考方案2】:

即使在您自己的代码中,如果您选择这样做,您也可以使用 null-forgiving 运算符传递 null。就编译器的可空性分析而言,null! 被认为是非空的。

【讨论】:

【参考方案3】:

总有人能做到

var myFoo = new Foo(null);

也许你可以使用领域驱动设计

public class Foo

    public Foo(string test)
    
         if (string.IsNullOrWhiteSpace(test))
             throw new ArgumentNullException(nameof(test));

         Test = test;
    
    public string Test get;private set;

【讨论】:

是的,你是对的,我猜这只是一个警告。我希望将来他们可以像在例如科特林【参考方案4】:

你是对的,其他没有使用新功能的代码可以将 null 分配给这个属性,没有运行时检查它只是编译器提示。

如果您想要运行时检查,您总是可以自己做:

public string Test  get; set if (value == null) throw new ArgumentNullException()  

请注意,您可以保证在大部分代码中不为空,您只需向***公共 API 添加防护并确保类被适当密封等。

当然,人们仍然可以使用反射来完善您的代码,但随后它就在他们身上

【讨论】:

所以这实际上意味着即使我确实使用了不可为空的类型,我仍然可以获得空引用异常,对吧? 嗯.... 不能在你编译的代码中,因为你有提示......但是其他人的代码没有提示开启,但参考您的代码 - 是的,他们可以获得空异常 好吧,例如自动映射器使用你的构造函数,或者类似的东西,仍然是你会得到异常:) Of course people can still use reflection to f*** your code up,真的,真的。您绝对可以使用反射来做到这一点,是否推荐,人们仍然这样做吗,是的。【参考方案5】:

为了处理空检查并使您的代码可读,我建议使用空对象设计模式。

更多阅读:

https://www.c-sharpcorner.com/article/null-object-design-pattern/

基本上,它涉及创建一个从相同接口派生并具有空实例的新对象。

例子:

public class NullExample : IExample  
  
    private static NullExample _instance;  
    private NullExample()  
       

    public static NullExample Instance  
      
        get   
            if (_instance == null)  
                return new NullExample();  
            return _instance;  
          
      

    //do nothing methods  
    public void MethodImplementedByInterface1()  
       

    public void MethodImplementedByInterface1()  
       
  

空值是不可避免的,但是可以干净地检查它们。

【讨论】:

以上是关于C# 8 中的不可为空的引用类型在运行时可以为空吗?的主要内容,如果未能解决你的问题,请参考以下文章

c#静态类中的8个可为空的引用类型

C# 8.0 和可为空引用类型

C# 8 中的 Non-Nullable 引用类型的默认值是多少?

Redshift:sortkey 和 distkey 可以为空吗?

C#中的?和??的用法

C#中的??是啥意思