class Bar {}
class Foo
{
Bar _bar;
public Foo(Bar bar)
{
// bar может быть null
if (bar == null)
throw new ArgumentNullException("bar");
_bar = bar;
}
}
С ростом проекта, подобных мест становиться все больше, и контроллировать создание исключений стоновиться сложнее. Одно из решений проблемы (для тех, кто пока еще не перешел на Code Contracts), сделать процесс создания исключений более централизованным, а именно в определенном классе - Guard (я остановился на названии Guard, но встречаются еще Assert, Check (проект KIGG CheckArgument.cs)):
public static class Guard
{
public static T AssertNotNull<T>(T paramValue, string paramName) where T : class
{
if (paramValue == null)
{
throw new ArgumentNullException(paramName);
}
return paramValue;
}
}
Принцип работы Guard следующий: если входной параметр удовлетворяет некому условию, то Guard пропускает параметр дальше, если нет, то будет сгенерированно исключение:
class Foo
{
Bar _bar;
public Foo(Bar bar)
{
// bar может быть null
_bar = Guard.AssertNotNull(bar, "bar");
}
}
Итак, за проверку входных параметров у нас отвечает Guard, поэтому вся логика проверки (в том числе более и сложная) не будет присутствовать в основном классе, а будет вынесена в Guard, что сохраняет компактность кода.
Во-вторых, мы получили удобный механизм контроля создания исключения, например мы легко сможем добавлять свои типы исключений или менять текст исключений и прочее.
И в-третьих, можно сделать так, чтобы Guard сам отдельно сообщал об исключениях (нарушение контракта):
public static class Guard
{
public static T AssertNotNull<T>(T paramValue, string paramName) where T : class
{
if (paramValue == null)
{
ArgumentNullException ex = new ArgumentNullException(paramName);
OnAlert(ex); // сообщим об исключении, например сделаем запись во внутренний лог.
throw ex;
}
return paramValue;
}
}
Так же иногда бывает удобно пометить Assert методы специальным атрибутом:
[DebuggerStepThrough]
public static T AssertNotNull<T>(T paramValue, string paramName) where T : class
{
//...
}
PS: Топик навеян статьей habrahabr: Концепция баррикады.
Синтаксически стало выглядеть красивее, но смысл от этого не поменялся -- все те же самые проверки, которые программист должен писать вручную.
ОтветитьУдалитьПроверки на null -- общая беда мейнстримовых языков, и решить их в пределах технологии C# или Java -- нереально. Все равно самостоятельно придется находить опасные места и дописывать туда обработку null-ов.
Особенно обидно, что технологии, позволяющей решить эту проблему, уже лет 50. Я говорю о семействе языков Lisp: Common Lisp, Scheme и Clojure. В ту же Clojure, например, такие проверки встроены в язык, и программисту ничего не нужно дополнительно делать вручную: http://clojuredocs.org/clojure_core/clojure.core/if-let.
Но даже не это важно. Главное, что если бы такой функциональности не было, на любом из лиспов реализовать ее -- раз плюнуть. Пример: http://my-clojure.blogspot.com/2011/01/java-null.html.
Я бы воздержался от таких резких комментариев и посоветовал автору поста выше почитать msdn
ОтветитьУдалитьhttp://msdn.microsoft.com/ru-ru/library/cc488527(v=vs.90).aspx
http://msdn.microsoft.com/ru-ru/library/ee256141.aspx
Это конечно не проверка параметров в методе, но развиваться может. По сути добавление нового атрибута может решить все проблемы, так что всё реально.
Из существующих "костылей" видел такое:
http://brainster.org/dev/dotnet-code-contracts/
@Andrey
ОтветитьУдалитьХорошо, тогда приведите пожалуйста пример валидации через DataAnnotations конкретно для моего примера.
@Andrey: Илья открывает возможность обработать некорректный параметр и по моему мнению делает это правильно.
ОтветитьУдалитьДля .net 4 мы получили класс контрактов
System.Diagnostics.Contracts Namespace
http://msdn.microsoft.com/en-us/library/dd287492.aspx
(cмотрите доку в самом низу страницы)
О Боже, ждать новой версии аж целого фреймворка для такой фигни, которую на нормальном языке добавить в этот самый язык -- дело двух минут... Blub-шиза в действии!..
ОтветитьУдалить