我偶然发现了一个有趣的问题,我没有任何解释,希望有人能解释一下.

在处理可为空的对象(伪)代码时,如下所示(注意:下面的工作片段):

object? obj = GetNullableObject();

if(obj is null) {
  return;
}

FunctionRequiringNonNullableObj(obj); // No compile error, object is known to be not null

然而,做同样的事情,比如Long?不会:

long? literal = GetNullableLong();

if(literal is null) {
  return;
}

FunctionRequiringNonNullableLong(literal); // Compiler error -> Cannot convert long? to long

不知何故,编译器似乎忽略了文字不能再为空的事实. 我想我遗漏了一些与性能优化有关的东西(或相关的).

实现了两个版本(对象?长?)来重现这个问题. 我的期望是,在这两种情况下,编译器都知道变量不再为空.

using System;
                    
public class Program
{
    public static void Main()
    {
        TestWithObject();
        TestWithLiteral();
    }
    
    private static void TestWithObject() {
        object? obj = ReturnOptionalObject();
        
        if(obj is null) {
            Console.WriteLine("Object is null. Returning.");
            return;
        }
        
        FunctionUsingObject(obj);
    }
    
    private static void TestWithLiteral() {
        long? literal = ReturnsOptionalLiteral();
        
        if(literal is null) {
            Console.WriteLine("Literal is null. Returning.");
            return;
        }
        
        // Does not work, compiler error
        // FunctionUsingLiteral(literal);
    }
    
    private static object? ReturnOptionalObject() {
        return new object();    
    }
    
    private static long? ReturnsOptionalLiteral () {
        return 123L;    
    }
    
    private static void FunctionUsingObject(object obj) {
        Console.WriteLine("Can access object.");
    }
    
    private static void FunctionUsingLiteral(long literal) {
        Console.WriteLine("Can access literal.");
    }
}

推荐答案

从编译器/运行时的Angular 来看,可空引用类型和可空值类型是完全不同的东西.可为Null的值类型实际上表示为Nullable<T> struct ,因此您需要访问Value属性:

long? literal = GetNullableLong();

if(literal is null) {
  return;
}

FunctionRequiringNonNullableLong(literal.Value);

另一方面,可为空的引用类型没有特定的类型来表示它们(只有一些元数据),并且仅用于可为空的静态分析(并且在默认情况下会产生警告,而不是错误).

两者都将在可为空的分析流方面正确运行,例如,如果您试图在NULL判断之前访问literal.Value,它将生成警告,试图对obj执行类似的操作.

另请注意:

Console.WriteLine(typeof(int?) == typeof(int)); // False
Console.WriteLine(typeof(int?) == typeof(Nullable<int>)); // True
// error CS8639: The typeof operator cannot be used on a nullable reference type
// Console.WriteLine(typeof(string?) == typeof(string)); 

阅读更多内容:

Csharp相关问答推荐

VS Code - C# - dotnet run找不到文件,但我可以打开并编辑它们吗?

如何打印已添加到List的Linq值,而不是C#中的:System.Collections.Generic.List ' 1[System.Int32]?

Blazor:用参数创建根路径

如何从顶部提取发票号作为单词发票后的第一个匹配

CS0103 dlibdotnet和www.example.com facerect不在上下文中

使用LayoutKind在C#中嵌套 struct .显式

从应用程序图API调用访问所有者字段

为什么EventInfo.EventHandlerType返回可为空的Type值?

我想在文本框悬停时在其底部显示一条线

.NET SDK包中的官方C#编译器在哪里?

C#DateTime.ParseExact不使用特定日期

反序列化私有成员

使用Dapper映射联接查询对象数据到使用SplitOn;

当索引和外键是不同的数据类型时,如何设置导航属性?

如何将端点(或с匹配请求并判断其路径)添加到BCL?

MSI无法将快捷方式添加到启动文件夹

如何在Cake脚本中设置MSBuild.exe的绝对路径

ASP.NET Core 8 Web API:如何添加版本控制?

如何对列表<;列表>;使用集合表达式?

如何处理ASP.NET Core中包含两个构造函数的控制器?