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

Can a non-nullable reference type in C# 8 be null in runtime?

在我看来,确实无法保证不可为 null 的变量永远不会为 null。假设我有一个 class,其中有一个 属性 不可为空:

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

现在看起来好像不能为空。但是,如果我们将此 class 引用到另一个不使用可空上下文的库,则没有什么可以阻止它在其中发送 null。

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

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

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

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

请注意,您可以保证在 大多数 代码中不为 null,您只需要在顶层添加守卫 Public API并确保 类 已妥善密封等

当然,人们仍然可以使用反射来搞砸你的代码,但那是他们的事

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

The compiler can't validate all calls to your public APIs, even if your code is compiled with nullable annotation contexts enabled. Furthermore, your libraries may be consumed by projects that have not yet opted into using nullable reference types. Validate inputs to public APIs even though you've declared them as nonnullable types.

总有人能做到

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;}
}

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

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

在这里阅读更多内容:

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

无法避免空值,但可以彻底检查它们。