使用 .NET 8 新引入的 Exception Throw Helper

使用 .NET 8 新引入的 Exception Throw Helper

Intro

在 .NET 6 中,引入了一个 ArgumentNullException.ThrowIfNull(object? argument, string? paramName = default) 的方法,在 .NET 7/8 中引入了更多的支持,我们可以在代码里使用这些 exception helper 来简化一些代码

Sample

最常用的 Argument exception 使用如下

ArgumentNullExceptionSample(null);

ArgumentExceptionThrowIfNullOrEmptySample(null);
ArgumentExceptionThrowIfNullOrEmptySample(string.Empty);

ArgumentExceptionThrowIfNullOrWhiteSpaceSample(null);
ArgumentExceptionThrowIfNullOrWhiteSpaceSample(string.Empty);
ArgumentExceptionThrowIfNullOrWhiteSpaceSample(" ");

public static void ArgumentNullExceptionSample(string? value)
{
    InvokeHelper.TryInvoke(() => ArgumentNullException.ThrowIfNull(value));
}

public static void ArgumentExceptionThrowIfNullOrEmptySample(string? value)
{
    InvokeHelper.TryInvoke(() => ArgumentException.ThrowIfNullOrEmpty(value));
}

public static void ArgumentExceptionThrowIfNullOrWhiteSpaceSample(string? value)
{
    InvokeHelper.TryInvoke(() => ArgumentException.ThrowIfNullOrWhiteSpace(value));
}

ArgumentNullException.ThrowIfNull(object? obj, string? paramName = default) 是 .NET 6 开始支持的

在 .NET 7 里支持了指针的判断 ArgumentNullException.ThrowIfNull(void* argument, string? paramName = default)(not CLS-compliant)

.NET 7 还引入了 ArgumentException.ThrowIfNullOrEmpty(string? value, string? paramName = default), 增加判断空字符串的场景

.NET 8 引入了 ArgumentException.ThrowIfNullOrWhiteSpace(string? value, string? paramName = default) 增强了判断空字符串的场景

输出结果如下:

19e0d97e3f23870f529ba4875a6e8c95.png

argument-exception-output-0

仔细看的话会发现,ArgumentException.ThrowIfNullOrEmpty/ArgumentException.ThrowIfNullOrWhiteSpace 在参数是 null 的情况下也会抛出 ArgumentNullException ,不是 null 则抛出 ArgumentException

比如 ThrowIfEmpty 的实现如下:

public static void ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
{
    if (string.IsNullOrEmpty(argument))
    {
        ThrowNullOrEmptyException(argument, paramName);
    }
}

private static void ThrowNullOrEmptyException(string? argument, string? paramName)
{
    ArgumentNullException.ThrowIfNull(argument, paramName);
    throw new ArgumentException(SR.Argument_EmptyString, paramName);
}

.NET 7 中引入了 ObjectDisposedException.ThrowIf 的两个方法

public static void ThrowIf (bool condition, object instance);
public static void ThrowIf (bool condition, Type type);

当 condition 条件为 true 时则会抛出 ObjectDisposedException 异常

ObjectDisposedException.ThrowIf 使用示例如下:

public static void ObjectDisposedExceptionSample()
{
    InvokeHelper.TryInvoke(() => ObjectDisposedException.ThrowIf(true, typeof(BackgroundService)));
    InvokeHelper.TryInvoke(() => ObjectDisposedException.ThrowIf(true, new GuidIdGenerator()));
}

输出结果如下:

06c4b6ed43db4d986a121b750bc8c59f.png

object-disposed-exception-output

目前无论是一个 Type 还是 instance 都会使用 type 的 full name 在信息里,感觉是 instance 的时候,错误信息里包含一下 instance 名称可能会更有帮助,实现如下:

[StackTraceHidden]
public static void ThrowIf([DoesNotReturnIf(true)] bool condition, object instance)
{
    if (!condition)
        return;
    ThrowHelper.ThrowObjectDisposedException(instance);
}

[StackTraceHidden]
public static void ThrowIf([DoesNotReturnIf(true)] bool condition, Type type)
{
    if (!condition)
        return;
    ThrowHelper.ThrowObjectDisposedException(type);
}

[DoesNotReturn]
internal static void ThrowObjectDisposedException(object? instance)
{
    throw new ObjectDisposedException(instance?.GetType().FullName);
}

[DoesNotReturn]
internal static void ThrowObjectDisposedException(Type? type)
{
    throw new ObjectDisposedException(type?.FullName);
}

ArgumentOutOfRangeExceptionSample 使用示例:

public static void ArgumentOutOfRangeExceptionSample()
{
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfZero(0));
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfNegative(-1));
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfNegativeOrZero( 0));
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfEqual(-1, -1));
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfNotEqual(-1, 0));
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfGreaterThan(1, 0));
    InvokeHelper.TryInvoke(() => ArgumentOutOfRangeException.ThrowIfLessThan(-1, 0));
}

输出结果如下:

18c83091646dbefb7758889b42d383fb.png

ArgumentOutOfRangeException-output

More

这些方法的目的是简洁地表达正在验证的约束条件,让系统在未满足约束条件时抛出一致的异常,同时优化成功和99.999%情况下不需要抛出异常。

这些方法在快速检查路径上尽可能少地进行工作,并将其他所有内容委托给执行实际抛出操作的方法(JIT不会内联该抛出方法,因为它将查看其实现并发现该方法总是会引发异常)。

并且都使用同一个 throw helper 方法可以减少 JIT 的工作,也会减少一些重复的 IL 代码

尝试下在你的代码里使用新增的 exception throw helper 吧~~

References

  • https://github.com/dotnet/runtime/pull/83853

  • https://github.com/dotnet/runtime/issues/82667

  • https://github.com/dotnet/runtime/issues/48573

  • https://github.com/dotnet/runtime/pull/71546

  • https://learn.microsoft.com/zh-cn/dotnet/api/system.argumentnullexception.throwifnull?view=net-8.0&WT.mc_id=DT-MVP-5004222

  • https://learn.microsoft.com/zh-cn/dotnet/api/system.objectdisposedexception.throwif?view=net-8.0&WT.mc_id=DT-MVP-5004222

  • https://learn.microsoft.com/zh-cn/dotnet/api/system.argumentexception.throwifnullorempty?view=net-8.0&WT.mc_id=DT-MVP-5004222

  • https://learn.microsoft.com/zh-cn/dotnet/api/system.argumentexception.throwifnullorwhitespace?view=net-8.0&WT.mc_id=DT-MVP-5004222

  • https://learn.microsoft.com/zh-cn/dotnet/api/system.argumentoutofrangeexception?view=net-8.0&WT.mc_id=DT-MVP-5004222

  • https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#exceptions

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/net8sample/Net8Sample/ExceptionThrowSample.cs

-

技术群:添加小编微信并备注进群

小编微信:mm1552923   

公众号:dotNet编程大全    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值