任何语言中的一个常见问题是断言发送到方法的参数满足您的要求,如果不满足,则发送好的,信息丰富的错误消息.这种代码反复重复,我们经常尝试为它创建帮助器.然而,在C#中,似乎那些帮助者被迫处理语言和编译器强加给我们的一些重复.为了表明我的意思,让我提供一些没有帮助的原始代码,然后是一个可能的帮助器.然后,我会指出帮助器中的重复并准确地说出我的问题.
首先,代码没有任何帮助:
public void SomeMethod(string firstName, string lastName, int age) { if(firstName == null) { throw new WhateverException("The value for firstName cannot be null."); } if(lastName == null) { throw new WhateverException("The value for lastName cannot be null."); } // Same kind of code for age, making sure it is a reasonable range (< 150, for example). // You get the idea }
}
现在,代码合理地尝试帮助:
public void SomeMethod(string firstName, string lastName, int age) { Helper.Validate( x=> x !=null, "firstName", firstName); Helper.Validate( x=> x!= null, "lastName", lastName); }
主要的问题是这样的:请注意代码如何具有传递参数的值和参数("名字"的名称和的firstName).这是错误消息可以说,"Blah blah blah firstName参数的值." 你有没有办法用反射或其他任何东西解决这个问题?还是一种减轻痛苦的方法?
更一般地说,您是否找到了其他方法来简化验证参数的任务,同时减少代码重复?
编辑:我读过人们谈论使用Parameters属性,但从来没有找到解决方法.谁有运气呢?
谢谢!
你应该查看代码合同 ; 他们几乎完全按照你的要求行事.例:
[Pure] public static double GetDistance(Point p1, Point p2) { CodeContract.RequiresAlways(p1 != null); CodeContract.RequiresAlways(p2 != null); // ... }
哇,我在这里找到了一些非常有趣的东西.Chris上面给出了另一个Stack Overflow问题的链接.其中一个答案指向一篇博文,其中描述了如何获取这样的代码:
public static void Copy(T[] dst, long dstOffset, T[] src, long srcOffset, long length) { Validate.Begin() .IsNotNull(dst, “dst”) .IsNotNull(src, “src”) .Check() .IsPositive(length) .IsIndexInRange(dst, dstOffset, “dstOffset”) .IsIndexInRange(dst, dstOffset + length, “dstOffset + length”) .IsIndexInRange(src, srcOffset, “srcOffset”) .IsIndexInRange(src, srcOffset + length, “srcOffset + length”) .Check(); for (int di = dstOffset; di < dstOffset + length; ++di) dst[di] = src[di - dstOffset + srcOffset]; }
我不相信这是在最好的答案,但可以肯定的是有趣的.这是来自Rick Brewster 的博客文章.
几周前我解决了这个确切的问题,因为我认为测试库似乎需要一百万个不同的版本Assert
才能使他们的消息具有描述性,这很奇怪.
这是我的解决方案.
简要总结 - 给出这段代码:
int x = 3; string t = "hi"; Assert(() => 5*x + (2 / t.Length) < 99);
我的Assert函数可以打印出传递给它的内容的以下摘要:
(((5 * x) + (2 / t.Length)) < 99) == True where { ((5 * x) + (2 / t.Length)) == 16 where { (5 * x) == 15 where { x == 3 } (2 / t.Length) == 1 where { t.Length == 2 where { t == "hi" } } } }
因此,所有标识符名称和值以及表达式的结构都可以包含在异常消息中,而无需在引用的字符串中重述它们.
这可能有点帮助:
按契约/ C#4.0设计/避免ArgumentNullException
好吧,伙计们,这又是我,我找到了令人惊讶和令人愉快的其他东西.这是另一个博客文章,上面提到的克里斯提到的其他SO问题.
这个人的方法让你写这个:
public class WebServer { public void BootstrapServer( int port, string rootDirectory, string serverName ) { Guard.IsNotNull( () => rootDirectory ); Guard.IsNotNull( () => serverName ); // Bootstrap the server } }
请注意,没有包含"rootDirectory"的字符串,也没有包含"serverName"的字符串!! 然而他的错误消息可以说"rootDirectory参数不能为空".
这正是我想要的,超出了我的期望.这是该家伙博客文章的链接.
实现非常简单,如下所示:
public static class Guard { public static void IsNotNull(Expression > expr) { // expression value != default of T if (!expr.Compile()().Equals(default(T))) return; var param = (MemberExpression) expr.Body; throw new ArgumentNullException(param.Member.Name); } }
请注意,这会使用"静态反射",因此在紧密循环或其他情况下,您可能希望使用上面的Rick Brewster方法.
我发布这篇文章后,我会立即投票给Chris,以及对另一个SO问题的回应.这是一些好东西!!!
使用我的图书馆The Helper Trinity:
public void SomeMethod(string firstName, string lastName, int age) { firstName.AssertNotNull("firstName"); lastName.AssertNotNull("lastName"); ... }
还支持断言枚举参数是正确的,集合及其内容是非的null
,字符串参数是非空的等等.有关详细示例,请参阅此处的用户文档.