16

你们都这样做:

public void Proc(object parameter)
{
    if (parameter == null)
        throw new ArgumentNullException("parameter");

    // Main code.
}

Jon Skeet 曾经提到他有时会使用扩展来做这个检查,所以你可以这样做:

parameter.ThrowIfNull("parameter");

所以我提出了这个扩展的两个实现,我不知道哪个是最好的。

第一的:

internal static void ThrowIfNull<T>(this T o, string paramName) where T : class
{
    if (o == null)
        throw new ArgumentNullException(paramName);
}

第二:

internal static void ThrowIfNull(this object o, string paramName)
{
    if (o == null)
        throw new ArgumentNullException(paramName);
}

你怎么看?

4

7 回答 7

13

为此,我倾向于坚持使用无处不在的Guard类:

static class Guard
{
    public static void AgainstNulls(object parameter, string name = null)
    {
        if (parameter == null) 
            throw new ArgumentNullException(name ?? "guarded argument was null");

        Contract.EndContractBlock(); // If you use Code Contracts.
    }
}

Guard.AgainstNulls(parameter, "parameter");

并且回避 extends object,加上肉眼看来,对null对象的方法调用似乎是荒谬的(尽管我知道对扩展方法进行 null 方法调用是完全有效的)。

至于哪个最好,我都不会用。它们都有无限递归。我也不会费心保护 message 参数,使其可选地为空。您的第一个解决方案也不支持Nullable<T>类型,因为class约束会阻止它。

当我们决定启用代码契约时,我们的Guard类也有Contract.EndContractBlock()它之后的调用,因为它符合所需的“if-then-throw”结构。

这也是PostSharp 方面的完美候选者。

于 2012-07-17T12:09:09.483 回答
5

我会用internal static void ThrowIfNull<T>(this T o, string paramName) where T : class. 我不会使用internal static void ThrowIfNull(this object o, string paramName),因为它可能会进行拳击。

于 2012-07-17T12:16:34.907 回答
4

我会这样做以避免硬编码参数名称。明天它可能会改变,然后你有更多的工作:

public static void ThrowIfNull<T>(this T item) where T : class
{
    var param = typeof(T).GetProperties()[0];
    if (param.GetValue(item, null) == null)
        throw new ArgumentNullException(param.Name);
}

并称之为:

public void Proc(object parameter)
{
    new { parameter }.ThrowIfNull(); //you have to call it this way.

    // Main code.
}

性能损失微不足道(在我的平庸计算机上,它在不到 25 毫秒的时间内运行了 100000 次),比通常看到的基于表达式的方法快得多

ThrowIfNull(() => resource);

一个这样的在这里。但是如果你负担不起那么大的打击,肯定不要使用这个。

您还可以将其扩展为对象的属性。

new { myClass.MyProperty1 }.ThrowIfNull();

您可以缓存属性值以进一步提高性能,因为属性名称在运行时不会更改。

另请参阅此问题:Resolving a parameter name at runtime

于 2013-02-03T22:07:48.557 回答
2

从 .NET 6 开始,现在我们ThrowIfNullSystem.ArgumentNullException类中拥有了具有以下签名的静态方法:

ThrowIfNull(object? argument, string? paramName = null);

因此,而不是写:

if (value == null)
{
    throw new System.ArgumentNullException(nameof(value));
}

现在我们可以简单地写:

System.ArgumentNullException.ThrowIfNull(value);

文档:https ://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception.throwifnull?view=net-6.0


这个新方法的实现利用属性System.Runtime.CompilerServices.CallerArgumentExpressionAttribute来进一步简化这一点,不需要开发人员显式地提供被保护的参数的名称。

最终引入这个新 API 的讨论可以在这里找到: https ://github.com/dotnet/runtime/issues/48573

在 .NET 6 代码库中引入它的 PR 可以在这里找到: https ://github.com/dotnet/runtime/pull/55594

于 2021-10-12T16:28:30.093 回答
0

使用表达式树怎么样(来自Visual Studio 杂志):

using System;
using System.Linq.Expressions;
namespace Validation
{
   public static class Validator
   {
     public static void ThrowIfNull(Expression<Func<object>> expression)
     {
       var body = expression.Body as MemberExpression;
       if( body == null)
       {
         throw new ArgumentException(
           "expected property or field expression.");
       }
       var compiled = expression.Compile();
       var value = compiled();
       if( value == null)
       {
         throw new ArgumentNullException(body.Member.Name);
       }
     }
     public static void ThrowIfNullOrEmpty(Expression<Func<String>> expression)  
     {
        var body = expression.Body as MemberExpression;
        if (body == null)
        {
          throw new ArgumentException(
            "expected property or field expression.");
        }
        var compiled = expression.Compile();
        var value = compiled();
        if (String.IsNullOrEmpty(value))
        {
          throw new ArgumentException(
            "String is null or empty", body.Member.Name);
        }
      }
   }

}

像这样使用:

public void Proc(object parameter1, object parameter2, string string1)
{
    Validator.ThrowIfNull(() => parameter1);
    Validator.ThrowIfNull(() => parameter2);
    Validator.ThrowIfNullOrEmpty(() => string1);
    // Main code.
}
于 2017-02-07T11:10:05.120 回答
0

基于 C# 10,我使用 ThrowIfNull 扩展方法:

public static class CheckNullArgument
{
    public static T ThrowIfNull<T>(this T argument)
    {
        ArgumentNullException.ThrowIfNull(argument);

        return argument;
    }
}

用法:

public class UsersController
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService.ThrowIfNull();
    }
}
于 2021-11-23T19:29:36.543 回答
-1

第二个似乎更优雅的处理方式。在这种情况下,您可以对每个托管对象进行限制。

internal static void ThrowIfNull(this object o, string paramName)
{
       if (o == null)
        throw new ArgumentNullException(paramName);
}
于 2012-07-17T12:10:10.370 回答