扩展空检查的最佳方法是什么?

时间:2012-07-17 12:05:19

标签: c# null extension-methods isnull

你们都这样做了:

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);
}

您怎么看?

5 个答案:

答案 0 :(得分: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");

并且避免扩展object,加上肉眼观察null对象上的方法调用似乎没有意义(尽管我知道对扩展方法进行空方法调用是完全有效的)。

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

我们的Guard课程在我​​们决定启用代码合同之后也会进行Contract.EndContractBlock()调用,因为它符合所需的“if-then-throw”结构。

这也是PostSharp aspect的完美候选人。

答案 1 :(得分:5)

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

答案 2 :(得分:4)

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

public static void ThrowIfNull<T>(this T item) where T : class
{
    if (item == null)
        return;

    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);

一个here。但如果你不能承受这么大的打击,肯定不要使用它。

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

new { myClass.MyProperty1 }.ThrowIfNull();

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

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

答案 3 :(得分:0)

如何使用表达式树(来自Visual Studio Magazine):

test()

}

像这样使用:

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);
        }
      }
   }

答案 4 :(得分:-1)

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

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