Рекомендации по проверке параметров

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

1) Риск (малая) производительность попадает и проверяет параметры в каждом из этих методов, даже если вы можете закончить проверку тех же параметров примерно 5 раз; или

2) Опасьте непредвиденное поведение и предположите, что, когда вы проверяете входные параметры, все другие возможные параметры, переданные и из вашего внутреннего кода, являются допустимыми (например, ни пустым, ни пустым)?

Изменить:. Чтобы привести пример, предположим, что у вас есть Regex RegexA и метод

internal bool Matches(string expression)
{
    return RegexA.IsMatch(expression);
}

IsMatch выдаст исключение из нулевого параметра, но не в пустую строку. Если вы заранее знаете, что пустая строка никогда не будет соответствовать этому регулярному выражению, вы должны использовать if (String.IsNullOrEmpty(expression)) раньше, даже зная, что он может быть проверен на недействительность внутри метода структуры IsMatch? В этом случае вы явно повторяете проверку, но лучше ли повторять это или рисковать?

Ответ 1

Обычно проверка параметров очень дешевая, даже если она называется тысячи раз. Например, проверьте, имеет ли значение значение null, строка или коллекция emtpy - число в заданном диапазоне.

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

Я бы также рекомендовал только проверять только общедоступные или защищенные методы. Обратите внимание, что все общедоступные методы с непроверенными параметрами потенциальные риски!

EDIT/другая мысль: Если метод не использует параметры, а просто передает его другому методу, вы можете также опустить проверку. Только метод, который фактически использует эти параметры для себя, должен выполнить проверку.

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

Ответ 2

Если проверка параметра не будет дорогостоящей, я бы пошел с №1. Неудачное поведение позволяет вам уловить ошибки за небольшую часть времени, что позволит вам сэкономить гораздо больше времени, чем требуется, чтобы записать несколько инструкций охраны в начале каждого метода.

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

Я лично пытался использовать Code Contracts и обнаружил, что для моих нужд было слишком много накладных расходов. Тем не менее, я оценил синтаксис, поэтому я сделал класс, чтобы помочь с этими инструкциями охраны, но который работает только во время выполнения. Он работает следующим образом:

public void ChangeUserName(int userId, string name)
{
    Require.ThatArgument(userId > 0);
    Require.ThatArgument(!string.IsNullOrWhitespace(name,
        () => "Usernames must be non-empty strings");
    var user = GetUser(userId);
    Require.That(user != null, 
        () => new UserDoesNotExistException("No user exists with ID " + userId));
    user.Name = name;
    ...
}

И одна последняя технология, которая помогает многим для этих проверок, - это аннотации Resharper. Например, рассмотрим следующий метод:

[CanBeNull]
public User GetUser(int userId)
{
    var user =  ... // Get the user from the db
    return user;
}

Говоря Resharper, что метод может вернуть нулевое значение, он будет знать, чтобы предупредить вас, если вы еще не выполнили нулевую проверку на user, прежде чем пытаться получить доступ к user.Name. Еще одна аннотация доступна, чтобы сообщить Resharper, что Require.That(user != null) представляет собой нулевую проверку. Вы также можете переписать свой метод следующим образом:

[NotNull]
public User GetUser(int userId)
{
    Require.ThatArgument(userId > 0);
    var user =  ... // Get the user from the db
    Require.That(user != null)
    return user;
}

Отметив этот метод как NotNull, Resharper может автоматически сказать вам, что user != null всегда будет разрешено true, поэтому вам не нужно его проверять. Есть все виды забавных вещей, которые вы можете сделать, чтобы упростить процедуру проверки.

Ответ 3

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

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

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

Ответ 4

Очень интересная тема:)

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

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

Ответ 5

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