Как избежать проверки аргументов - PullRequest
5 голосов
/ 22 октября 2009

Проверка примитивных аргументов и «сложных данных»

Проверка аргументов

При написании метода аргументы должны проверяться в первую очередь перед выполнением каких-либо операций. Например, допустим, у нас есть класс, представляющий людей:

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? имя и возраст не проверяются до того, как их значения заданы как поля 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 (....) в качестве аргумента ... во всех ваших методах.

Что мы можем сделать, чтобы избежать написания String.IsNullOrEmpty несколько раз в вашем коде?

Ответы [ 5 ]

6 голосов
/ 22 октября 2009

Вы можете ознакомиться с Кодексами контрактов в .NET 4.0 .

Вы также можете обратиться к библиотеке FluentValidation на CodePlex , если не хотите ждать контрактов кода.

В конечном счете, вам все равно нужно где-то поместить правила, управляющие значениями аргументов, - это просто вопрос выбора предпочитаемого вами стиля (например, string.IsNullOrEmpty) или декларативного.

Проверка ваших входных данных является ключевой практикой написания твердого кода, но, безусловно, он может быть многократным и подробным.

2 голосов
/ 22 октября 2009

Вам может помочь использование более сложных типов, а не примитивов.

Например, если вы потратите время, чтобы определить что-то вроде класса PersonName, вы можете получить подтверждение там , и вам не нужно будет проверять его на каждом другом объекте, который нуждается иметь имя на нем.

Очевидно, что это только поможет решить проблему, если у вас есть несколько объектов, которые используют одинаковые типы полей.

1 голос
/ 22 октября 2009

Есть варианты, основанные на Postsharp . code-o-matic является одним из них. Это позволяет вам написать код, подобный этому:

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

Я использую это каждый день на работе.

1 голос
/ 22 октября 2009

Вы можете попробовать использовать Framework 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

0 голосов
/ 22 октября 2009

Я дам решение на языке программирования 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.
    }
}
...