复杂类型的方法参数验证

时间:2015-07-25 00:20:29

标签: c# validation arguments complextype

我是方法中参数验证的粉丝。这样的事情在我的代码中很常见:

public void Foo(string someString)
{
    #region parameter validation
    if(string.IsNullOrWhiteSpace(someString))
        throw new ArgumentNullException("someString");
        // Let's ignore the .NET 4.6 nameof-feature for the sake of this question.
    #endregion

    // do things
}

现在让我们假设该方法不接受字符串,而是接受复杂的对象。假设我希望该方法验证是否设置了此复杂对象的属性。这样做的正确方法是什么?到目前为止,我已经这样做了:

public void Foo(Person person)
{
    #region parameter validation
    if(person == null)
        throw new ArgumentNullException("person");
    if(string.IsNullOrWhiteSpace(person.Name))
        throw new ArgumentNullException("person.Name");
    #endregion

    // do things
}

这是否有一些最佳做法? ArgumentNullException是正确的方法吗?是否建议检查这样的属性?

4 个答案:

答案 0 :(得分:1)

在我的情况下,我的所有模型都有一个Validate方法,这样我只是调用它,如果它返回false(这意味着我有错误)我调用另一个方法来从中获取错误并显示给用户,像这样:

if (person == null)
    throw new ArgumentNullException (nameof (person));

// Validates only 1 property.
if (!person.Validate (nameof (person.MyProperty)))
    DisplayErrors (person);

// Validates all properties.
if (!person.Validate ())
    DisplayErrors (person);

背后的原因是,在大多数情况下,我必须允许模型在用户填写信息时无效。我将这些方法与WPF接口INotifyDataErrorInfo一起使用,以便在发生错误时刷新UI。

当方法或操作只需要部分对象时,外观模型是创建包含更复杂对象的新类的解决方案,而外观类也使用Validate方法进行部分验证。 / p>

答案 1 :(得分:1)

您可以考虑code contracts。当然,正如您所示,检查验证参数是一种非常好的方法...假设您只验证正在使用的字段。验证您没有引用的字段会引发麻烦(除非这是该方法的明确目的)。

最新版本的C#提供了一个'?。'运算符,它可以让你做这样的事情:

int? length = customers?.Length;

string name = person?.Name;

这允许您保护自己免受空引用异常的影响,并且在某些情况下可能优于预验证。

哦,运行代码分析工具通常会在验证之前使用参数进行标记,因此至少这些工具认为它是最佳实践。

更新(以回应评论): 这在很大程度上取决于你想要做什么。如果您正在创建API并希望尽可能返回最佳错误消息,那么您所建议的可能就是您可以做的最好的。这假设您主要关注与API的消费者沟通。

如果您的主要兴趣是确保提供给您的方法的数据有效并且不会导致您的方法行为不良(CYA),那么您应该查看合同。合同的优点是提供后置条件(确保您的方法没有返回意外)和不变条件(确保避免改变您没有想要的东西)。合同可以提供静态(编译时)保证以及运行时保证。有一种相当精细的方式来管理合同选项。运行时合同违规会导致人们可能不太熟悉的合同例外情况。用户也可能不熟悉编译时间投诉。 这假设您主要关注的是确保正确操作或不会因滥用您的API而受到指责。

您可以同时使用这两种方法,例如使用“合同”进行编译时检查,以及更简单的“如果'检查运行时。

答案 2 :(得分:0)

对我来说,99%的案件听起来像是一个设计问题。一个人没有名字有效吗?我怀疑不是。如果没有,为什么你不允许创建一个?如果你(出于某种原因)确实允许它,那么对象的责任就不是保持它自己的有效性"?我会依赖数据注释之类的东西,用元数据标记所需的状态,并简单地检查传入的人是否有效"

答案 3 :(得分:0)

这似乎没有正确的方法,所以我会坚持自己的方式,这是

if(myComplexObject.SomeProperty == null)
    throw new ArgumentNullException("myComplexObject.SomeProperty");

来电者会立即知道出了什么问题。

请注意,我不使用nameof-operator,因为它再次只给我SomeProperty而不是myComplexObject.SomeProperty。我可以做nameof(myComplexObject) + "." + nameof(myComplexObject.SomeProperty,但我需要多考虑一下,第一眼看上去不正确。

以下是我认为其他方法比我更差的原因:

有人说我应该在模型中验证,我不会这样做,因为模型不知道对象必须如何验证它不知道的方法。如果它是整体对象实例的有效性,请确保将其粘贴在模型中,或者如果您甚至要求我使用业务层进行业务验证,但事实并非如此。

其他人说我可以在C#中使用null-coalescing运算符(?.),我也不会这样做,因为它不是“如果为null然后只使用那个”场景,而是“如果这是null,我不会做任何事情,你应该感觉不好给我null,修复代码“方案。

然后是代码契约,它提供了验证参数的一种甜蜜方式,并且有一些更好的好处,但最终出现了同样的问题:如何告诉调用者复杂对象的确切值是无效的?他需要知道论证Y的属性X是错误的,不能少。虽然代码合同不是我的问题的答案,但我认为如果出现同样的情况,我的答案也可以在那里使用。

然后有一个建议,我不应该验证那个地方的对象的属性,而是在需要它的函数中。问题是,该属性几乎不会被作为单个分隔值传递给另一个函数。此外,它会对接口的想法产生影响。