扩展空检查的最佳方法是什么?
你这都是这样的:
public void Proc(object parameter) { if (parameter == null) throw new ArgumentNullException("parameter"); // Main code. }
Jon Skeet曾经提到他有时会使用扩展来进行检查,所以你可以做到:
parameter.ThrowIfNull("parameter");
所以我得到了这个扩展的两个实现,我不知道哪个是最好的。
第一:
internal static void ThrowIfNull(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); }
你怎么看?
我使用internal static void ThrowIfNull
。 我不会使用internal static void ThrowIfNull(this object o, string paramName)
因为它可能会进行装箱。
我倾向于坚持无处不在的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
类型,因为class
约束会阻止它。
我们的Guard
类也在它之后调用Contract.EndContractBlock()
,当我们决定启用Code Contracts时,因为它适合所需的“if-then-throw”结构。
这也是PostSharp方面的完美候选者。
我会这样做以避免硬编码参数名称。 明天它可以改变,然后你还有更多的工作:
public static void ThrowIfNull(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);
一个这样的。 但是,如果你不能承受这么大的打击,肯定不要使用它。
您还可以为对象的属性扩展它。
new { myClass.MyProperty1 }.ThrowIfNull();
您可以缓存属性值以进一步提高性能,因为属性名称在运行时不会更改。
另外请参阅此问题: 在运行时解析参数名称
如何使用Expression Trees(来自Visual Studio Magazine ):
using System; using System.Linq.Expressions; namespace Validation { public static class Validator { public static void ThrowIfNull(Expression> 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> 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. }
第二个似乎更优雅的处理方式相同。 在这种情况下,您可以对每个托管对象设置限制。
internal static void ThrowIfNull(this object o, string paramName) { if (o == null) throw new ArgumentNullException(paramName); }