我正在try 定义一个函数,该函数返回传入的值的可空版本,然后将其与布尔值一起apply.

using System;

#nullable enable

public class Program
{
    public static void Main()
    {
        Console.WriteLine(Test(true, true) ?? false);
    }
    
    public static A? Test<A>(A val, bool useNull) =>
        useNull ? default(A?) : val;
}

然而,这给了我一个错误:

编译错误(第11行,第21列):运算符‘??’不能应用于‘bool’和‘bool’类型的操作数

但我不太清楚why是否给出了这个错误--毕竟Test()应该返回类型bool?,而不是bool,所以使用??应该是完全合法的.

这是C#可空值特性中的一个错误,还是我误解了它的工作原理?

无论如何,如果我将Test()重写为非通用的,它似乎是有效的:

public static bool? Test(bool val, bool useNull) => 
    useNull ? null : val;

但我试图理解为什么通用版本不起作用.

推荐答案

首先:一旦你在两个单独的函数中分别为值引用类型使用类型约束classstruct,你就可以让它工作了.

namespace MyProgram;

public class Program
{
    public static void Main()
    {
        Console.WriteLine(NullableValueType(true, true) ?? false);
        Console.WriteLine(NullableReferenceType(new object(), true) ?? false);
    }

    public static A? NullableValueType<A>(A val, bool useNull) where A : struct
        => useNull ? null : val;


    public static A? NullableReferenceType<A>(A val, bool useNull) where A : class
            => useNull ? null : val;
}

现在,为什么这不能在没有类型约束的单个方法中工作?

这与值类型的可空特性的实现方式有关,而不是引用类型.引用类型在设计上是可以为空的(这里的可为空功能的实现基本上只是一组属性,编译器可以使用这些属性来区分哪些可以为空,哪些不可以为空),而值类型不能为空.事实上,可为空值类型本身永远不会是null.它是使用Nullable<T> struct 和boxing实现的.

当您查看编译器使用例如Sharplab.io(这里是C#编译器从上面的代码生成的相关摘录)生成的lowered C#时,情况会变得更加清楚:

namespace MyProgram
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine(NullableValueType(true, true).GetValueOrDefault());
            Console.WriteLine(NullableReferenceType(new object(), true) ?? ((object)false));
        }

        public static Nullable<A> NullableValueType<A>(A val, bool useNull) where A : struct
        {
            return useNull ? null : new Nullable<A>(val);
        }

        [System.Runtime.CompilerServices.NullableContext(1)]
        [return: System.Runtime.CompilerServices.Nullable(2)]
        public static A NullableReferenceType<A>(A val, bool useNull) where A : class
        {
            return useNull ? null : val;
        }
    }
}

正如您所看到的,由于实现可为空的值/引用类型的方式不同,返回类型也不同,因此您不能将其统一到单个方法中.

免责声明:当我看到这个问题时,我深入研究了这个问题,因为我一开始也对这种行为感到惊讶,这是我看了下面的代码后得出的解释.如果需要,可以随意添加/更正.

Csharp相关问答推荐

我无法在Ubuntu下编译使用microsoft.extension.configurationbuilder jsonapi和mono mcs的c#应用程序

Microsoft. SQLServer. Types(106.1000.6)在try 从用户定义的类型检索值时引发异常

EF Core在请求列表时忽略列,但在按ID获取时包含

Unity中的刚体2D运动

REST API端点中异步后台代码执行的类型

Nuget包Serilog.Sinks.AwsCloudwatch引发TypeLoadExceptions,因为父类型是密封的

在C#中,有没有一种方法可以集中定义跨多个方法使用的XML参数描述符?

从依赖项容器在.NET 8中的Program.cs文件中添加IOC

如何将字符串变量传递给JObject C#-无法加载文件或程序集';System.Text.Json

什么时候接受(等待)信号灯?尽可能的本地化?

带有可选参数的模拟方法返回意外的不同值,具体取决于可选的默认值

为什么@rendermode Interactive Auto不能在.NET 8.0 Blazor中运行?

在implementationFactory中避免循环依赖

WinUI 3中DoubleCollection崩溃应用程序类型的依赖属性

{ or ; expected error如何解决此问题

DropDownListFor未显示选定值

将两个for循环更改为一条LINQ语句

Unity 3D-意外轴捕捉和未知力对脉冲react 行为的影响

Avalonia MVVM数据模板

如何在flutter dart中使用publicKey.xml文件进行rsa加密,我遇到了问题Exception:Could not parse BigInt