如何将可空类型隐式转换为不可空类型

Posted

技术标签:

【中文标题】如何将可空类型隐式转换为不可空类型【英文标题】:How to implicitly convert nullable type to non-nullable type 【发布时间】:2022-01-22 13:52:40 【问题描述】:

我有一个可以为空的 c# 10 .net 6 项目,其扩展方法为 ThrowIfNull

using System;
using System.Runtime.CompilerServices;

#nullable enable
public static class NullExtensions

    public static T ThrowIfNull<T>(
        this T? argument, 
        string? message = default, 
        [CallerArgumentExpression("argument")] string? paramName = default
    )
    
        if (argument is null)
        
            throw new ArgumentNullException(paramName, message);
        
        else
        
            return argument;
        
    

扩展方法将string?隐式转换为string,但它不适用于int?bool?等其他原始类型

public class Program

    public static void Main()
    
        Console.WriteLine("Hello World");
        
        string? foo = "foo";
        string nonNullableFoo = foo.ThrowIfNull();  // success from "string?" to "string"
        Console.WriteLine(nonNullableFoo);
        
        bool? baz = true;
        bool nonNullableBaz = baz.ThrowIfNull();    // success from "string?" to "string"
        Console.WriteLine(nonNullableFoo);
        
        int? bar = 2;
        int nonNullableBar = bar.ThrowIfNull(); // error: Cannot implicitly convert type 'int?' to 'int'
        Console.WriteLine(nonNullableBar);
    

如何使扩展隐式转换int?bool?

这是完整的 dotnet fiddle https://dotnetfiddle.net/LiQ8NL

【问题讨论】:

【参考方案1】:
使用空合并运算符??

要将可空值分配给非空变量,请考虑以下代码:

int? value = 28;
int result = value ?? -1;
Console.WriteLine($"The result is result");
输出:The result is 28
int? value = null;
int result = value ?? -1;
Console.WriteLine($"The result is result");
输出:The result is -1
编辑代码

重新排列代码如下。因此bool?可以隐式使用该类型:

int? bar = 2;
int nonNullableBar = bar ?? -1; 
Console.WriteLine(nonNullableBar);
bool? baz = true;
bool nonNullableBaz = false;
        
if (baz == true) 
    nonNullableBaz = true;
 
else if(baz == false) 
    nonNullableBaz = false;
 
else 
    /* Something */


参考文献
Nullable Value Types Boolean Logical Operators

【讨论】:

这将一个可空值(int?)显式转换为相同的可空值(int?),这不是我想要的。我想将一个可空值(int?)隐式转换为不可空值(int) 这是我第一次遇到这种用法。这就是您正在寻找的答案。 分配像 -1 这样的魔法值来表示 null 并不是一个好习惯。 你能解释一下原因吗? 我现在快速回顾了关于这个主题的良好实践,但没有遇到任何关于劣势的提及。我会进一步调查此事。【参考方案2】:

您可以通过为不可为空的引用类型提供一种扩展方法和为非托管(例如 int、bool、...)类型提供另一种扩展方法来实现您的目标。请注意,非托管类型需要强制转换。

public static class NullExtensions

    public static T ThrowIfNull<T>(
        this T? argument,
        string? message = default,
        [CallerArgumentExpression("argument")] string? paramName = default
    ) where T : notnull
    
        if (argument is null)
        
            throw new ArgumentNullException(paramName, message);
        
        else
        
            return argument;
        
    

    public static T ThrowIfNull<T>(
        this T? argument,
        string? message = default,
        [CallerArgumentExpression("argument")] string? paramName = default
    ) where T : unmanaged
    
        if (argument is null)
        
            throw new ArgumentNullException(paramName, message);
        
        else
        
            return (T)argument;
        
    

这样使用:

int? foo = 42;
int bar = foo.ThrowIfNull();
Console.WriteLine(bar);

string? baz = "Hello";
string quus = baz.ThrowIfNull();
Console.WriteLine(quus);

// Comment out either this
baz = null;
quus = baz.ThrowIfNull();
// Or this
foo = null;
bar = foo.ThrowIfNull();

【讨论】:

TIL:notnullmanaged。接受这个作为答案。谢谢!供将来参考的完整小提琴在这里dotnetfiddle.net/fjwIo7【参考方案3】:

在 MS dotnet docs https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters 中找到一种可能的选项

约束 "where T : struct" 类型参数必须是不可为空的值类型

同样根据这个答案https://***.com/a/8745492/2877168 不可能使string? 和所有其他值类型工作。所以唯一的办法就是定义两个扩展方法。这是适用于string?int?bool? 等的更新版本

public static class NullExtensions

    public static T ThrowIfNull<T>(this T? argument, string? message = default, [CallerArgumentExpression("argument")] string? paramName = default)
        where T : struct
    
        if (argument is null)
            throw new ArgumentNullException(paramName, message);
        return (T)argument;
    

    public static string ThrowIfNull(this string? argument, string? message = default, [CallerArgumentExpression("argument")] string? paramName = default)
    
        if (argument is null)
            throw new ArgumentNullException(paramName, message);
        return argument;
    

此版本的工作版本位于 https://dotnetfiddle.net/uBX1w6

【讨论】:

以上是关于如何将可空类型隐式转换为不可空类型的主要内容,如果未能解决你的问题,请参考以下文章

C# winforms:将可空类型绑定到其他属性(不是文本)时出错

在可空类型上使用合并空运算符会更改隐式类型

如何将十进制变量转换为可空类型

可空类型

可空引用类型和选项模式

在C#中,如何将键盘的空输入转换为可空类型的布尔变量?