在 C# 8 中,非空引用类型在运行时是否可以为 null?

14

在我看来,确实没有任何保证一个不可为空的变量绝不会为null。想象一下,如果我有一个类,其中有一个属性是不可为空的:

public class Foo
{
    public Foo(string test)
    {
        Test = test;
    }
    public string Test {get;set;}
}

现在看起来好像它现在不能为空。但是如果我们用另一个不使用可空上下文的库引用此类,它可以发送null值。

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


public void Foo(string test){...} 还是 public Foo(string test){...} - huMpty duMpty
谢谢,我已经修复了。这就是当人过于依赖R#生成构造函数时会发生的事情 :) - Ilya Chernomordik
3
C# 9很可能会添加简化的空值验证 - Steven
2
简而言之,“可空引用类型”功能完全失效。 - Alejandro
5个回答

12

1
微软从链接中删除了那个部分真是遗憾。感谢您在回答中保留它。 - krillgar

3
即使在你自己的代码中,如果你选择这样做,你可以使用null-forgiving运算符传递null。对于编译器的nullability分析来说,null!被认为不是null。

2

您说得对,没有使用新功能的其他代码可能会将null赋值给此属性,没有运行时检查,只是编译器提示。

如果您想进行运行时检查,可以随时自己完成:

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

请注意,您可以在大多数代码中保证不为空,只需要在顶级公共API中添加守卫,并确保类被适当封装等。
当然,人们仍然可以使用反射来破坏您的代码,但那时就是他们的责任了。

这实际上意味着,即使我使用了非空类型,仍然可能会出现空引用异常,对吗? - Ilya Chernomordik
嗯...在编译代码时,不能这样做,因为你已经有了提示...但是其他人的代码没有提示,但引用了你的代码 - 是的,他们可能会得到一个空异常。 - Milney
如果例如自动映射器使用了您的构造函数或类似的东西,仍然会抛出异常 :) - Ilya Chernomordik
当然,人们仍然可以使用反射来搞乱你的代码,没错,确实如此。你绝对可以使用反射来做到这一点,但是推荐吗不推荐,但人们仍然会这样做。 - Trevor

2

有人总是可以做到的

var myFoo = new Foo(null);

也许你可以使用领域驱动设计(Domain Driven Design)来解决这个问题。
public class Foo
{
    public Foo(string test)
    {
         if (string.IsNullOrWhiteSpace(test))
             throw new ArgumentNullException(nameof(test));

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

是的,你说得对,这只是一个警告而已。我希望将来他们可以像 Kotlin 一样真正地执行它。 - Ilya Chernomordik

-2
为了处理空值检查并使您的代码更易读,我建议使用Null Object设计模式。
更多阅读请点击这里:

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()  
    { }  
}  

空值无法避免,但可以进行清晰的检查。


第一次访问“Instance”属性时,不应该将“_instance”成员设置为“new NullExample()”。所引用的文章在其单例模式中似乎有相同的错误。按照当前的编写方式,每次访问“Instance”属性都会创建一个新的“NullExample”实例,而不是重用第一个实例。 - Zarepheth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接