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

7

我有一个可为空的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?? 以下是完整的dotnetfiddle:https://dotnetfiddle.net/LiQ8NL
3个回答

6

您可以为非可空引用类型提供一个扩展方法,为非托管类型(例如 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();

1
TIL: notnullmanaged。接受这个答案。谢谢!完整的编辑器链接以供日后参考在这里https://dotnetfiddle.net/fjwIo7 - Ssh Quack

3
使用空值合并运算符??

如果要将可空值赋给非空变量,可以考虑以下代码:

int? value = 28;
int result = value ?? -1;
Console.WriteLine($"The result is {result}");
  • 输出:结果为28
int? value = null;
int result = value ?? -1;
Console.WriteLine($"The result is {result}");
  • 输出: 结果是-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 */
}

参考资料

这是将可空值(int?)显式转换为相同的可空值(int?),这不是我想要的。我想将可空值(int?)隐式转换为非可空值(int)。 - Ssh Quack
将像-1这样的魔数赋值给null并不是一个好的编程实践。 - Eric J.
你能解释一下为什么吗? - Sercan Sebetçi
如果输入-1是有效的呢?当你考虑bool?时,这个问题尤为明显。 - Eric J.
1
我已经快速地审查了这个主题的良好实践,但没有发现任何不利的参考。我将进一步调查此事。 - Sercan Sebetçi
显示剩余2条评论

0

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

约束 "where T : struct" 类型参数必须是非空值类型

根据这个答案 https://dev59.com/sWoy5IYBdhLWcg3wJKvR#8745492,不可能使其适用于 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上。


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