使用 .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)
增强了判断空字符串的场景
输出结果如下:
仔细看的话会发现,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()));
}
输出结果如下:
目前无论是一个 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));
}
输出结果如下:
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编程大全