5

验证原始参数和“复杂数据”

验证参数

编写方法时,应先验证参数,然后再执行任何操作。例如,假设我们有一个代表人的类:

public class Person
{
    public readonly string Name;
    public readonly int Age;

    public class Person(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }
}

这个 Person 类有什么问题?name 和 age 在它们的值被设置为 Person 的字段之前不会被验证。“已验证”是什么意思?应检查这两个参数是否可以接受它们的值。例如,如果 name 的值为空字符串怎么办?或者年龄的值为-10?

当值不可接受时,通过抛出 ArgumentExceptions 或派生异常来验证参数。例如:

public class Person(string name, int age)
{
    if (String.IsNullOrEmpty(name))
    {
        throw new ArgumentNullException
            ("name", "Cannot be null or empty.");
    }

    if (age <= 0 || age > 120)
    {
        throw new ArgumentOutOfRangeException
            ("age", "Must be greater than 0 and less than 120.");
    }

    this.Name = name;
    this.Age = age;
}

这正确地验证了 Person 的构造函数接收的参数。

乏味的恶心

因为您已经验证参数很长时间了(对吗?),您可能已经厌倦了在所有方法中编写这些 if (....) throw Argument... 语句。

我们可以做些什么来避免在整个代码中多次编写 String.IsNullOrEmpty?

4

5 回答 5

6

您可以查看.NET 4.0 中的代码合同

如果您不想等待代码合同,您可能还想查看CodePlex 上的 FluentValidation 库。

最终,您仍然需要将管理参数值的规则放在某个地方——这只是决定您更喜欢命令式风格(例如 string.IsNullOrEmpty)还是声明式风格的问题。

验证你的输入是编写可靠代码的关键实践——但它肯定是重复的和冗长的。

于 2009-10-22T17:26:15.840 回答
2

使用更复杂的类型而不是原语可能会对您有所帮助。

例如,如果您花时间定义PersonName类之类的东西,您可以在那里进行验证,并且您不必在每个其他需要有名称的对象上继续验证它。

显然,如果您有多个使用相同字段类型的对象,这只会帮助解决问题。

于 2009-10-22T17:39:56.110 回答
1

您可以尝试使用 Castle Validation Framework => http://www.castleproject.org/activerecord/documentation/v1rc1/usersguide/validation.html

或者

您可以使用我创建的简单验证框架。这两个框架都使用基于属性的验证。查看以下链接:

http://www.highoncoding.com/Articles/424_Creating_a_Domain_Object_Validation_Framework.aspx

于 2009-10-22T18:07:04.590 回答
1

有基于Postsharp的选项。code-o-matic就是其中之一。它允许您编写如下代码:

public class Person(
    [NotNull, NotEmpty] string name,
    [NotNull, NotEmpty] int age
)
{
    this.Name = name;
    this.Age = age;
}

我每天在工作中都使用它。

于 2009-10-22T18:12:59.233 回答
-2

我将用 D 编程语言给出一个解决方案。我不知道 C# 泛型和可变参数有多强大,因为我不使用 C#,但也许你可以调整一下:

void validate(T...)(T args) {  // args is variadic.
    foreach(arg; args) {  // Iterate over variadic argument list.
        static if(isSomeString!(typeof(arg))) {  // Introspect to see arg's type.
            if(arg.isNullOrEmpty) {
                throw new ArgException(
                    "Problem exists between keyboard and chair.");
            }
        } else static if(isOtherTypeWithBoilerPlateValidation!(typeof(arg))) {
            // Do more boilerplate validation.
        }
    }
}

用法:

class Foo {
    SomeType myMethod(T arg1, U arg2, V arg3) {
        validate(arg1, arg2, arg3);

        // Do non-boilerplate validation.

        // Method body.
    }
}
于 2009-10-22T17:31:33.443 回答