Реклама
Комментарии 33
По тэгу "C#" выдаёт 404-ю ошибку. Это, наверное, к разработчикам, а автору думаю стоит добавить синоним "C sharp"
Если вы используете этот подход только для контролов, то я бы эту логику по проверке запихнул в какой-то свой базовый контрол, который это реализует.
Или это тоже кусочек только из общей реализации?
Нет, это законченное решение для любого класса. И не только в контексте asp.net.
Цель как понятно из текста, проверить валидность свойств и полей экземпляра класса одним махом.
Просто для меня наиболее востребованным этот механизм стал в asp.net. Работа такая :).
Чем не подходит механизм с валидаторами?

По коду:
1) Пустой конструктор можно и не объявлять.
2) Если используется конструктор по умолчанию, можно писать вместо [CheckNull()] - [CheckNull] (без скобок).
3) В методе Check можно не указывать BindingFlags.DeclaredOnly. Тогда вроде бы не будет необходимости делать вызов GetType().BaseType при вызове метода.
4) Проверку при !IsPostBack, наверное, надо убрать? Ведь будет при загрузке страницы исключение выкидываться.
5) Механизм исключений лучше заменить на возврат IEnumerable , где IBrokenRule описывает ошибку.
Спасибо за комментарий.
Первый и второй пункт дело вкуса. Мне лично больше нравится присутствие пустого конструктора, потому-что атрибут не выглядит так одиноко. :)
Третий пункт - если так сделать, то p_type.FindMembers вернет уйму (правда, очень много) наследуемых полей и свойств. Я посчитал, что это не продуктивно.
Четвертый пункт - немного не понял, вы предлагаете отследить postback? Я оставляю такие вопросы тому, кто будет использовать код.
Пятый пункт. Мне в данном контексте больше нравятся исключения. Если вы предложите реализацию другого варианта, буду только рад.
1, 2. Писать ненужный код - это не дело вкуса. Например, пустая строка между двумя методами - это нужный "код". А вот три пустых строки - совершенно лишний. По моему скромному мнению, конечно же.

3. Ну, продуктивно - сделать работающее решение даже для иерархии наследования.
4. Я не предлагаю, мне интересно, что будет делаться при первой загрузке. Если попытаться запустить этот пример таким, какой он есть, то (мне кажется) выбросится исключение, поскольку никто этому полю не присваивает значение при первой загрузке. Мне кажется, что этот момент надо оговаривать, что, например, "необходимо самостоятельно инициализировать при первой загрузке поля, помеченные атрибутом, корректными значениями".
5. На тему исключений написано множество статей. В случае запуска метода, проверяющего на ошибки, выглядит странным использование исключений, т.к. вызывающий код ожидает чего-то не того.

Валидаторы - это контролы. Вы ведь статью написали в рамках ASP.NET, а не написания приложений вообще.
1,2. Все равно компилятор вставит конструктор по умолчанию. Говорю же дело вкуса.
3. Согласен, такое решение имеет право на жизнь. Так же как и мое.
4. Значение присваивается вот здесь:

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

<uc3:BranchHint ID="BranchHint1" runat="server" BranchId=”12” />
Ясно.

Насчёт исключений. Есть не два способа обработки ошибок - исключения и возврат кода ошибки, их больше. Тут вопрос следующий - если этот механизм нужен для проверки во время разработки, то всё понятно. Но если проверяется корректность ввода, то исключения, на мой взгляд, подходят слабо - это стандартная проверка некторых бизнес-правил, а не исключительная (вдумайтесь в само слово) ситуация.
Риторика и философия... нарушение бизнес-правил - это исключительная ситуация. Как вам такое? Я за толерантность, используйте то, что удобно и к чему привыкли.
Скажем так, если на собеседовании я услышу такое объяснении, я мысленно поставлю минус, ибо эта самая "философия и риторика" мешает потом совместной работе.

Конечно, Вы можете использовать то, что Вам удобно и Вам привычно. Я лишь высказываю своё мнение.
Кстати, расскажите мне как вы решаете задачу с работой строки запроса?
Безотносительно: ни в одном моем посте никто ни разу не привел примеры того, как они сами что-то делают. Наверное, конечно, не делают такого, но хоть тут-то должны быть альтернативные варианты. Уж Request то все используют.
Я в соседней ветке писал про Request.

Вкратце, суть в том, что old-school asp.net говорит о том, что нужно использовать event-модель. Т.е. для любого активного элемента на странице есть контрол. Этот контрол берёт на себя всю логику по обработке POST-запросов.

Такая модель не очень-то применима для нормальных интернет-проектов, где пользователь может для своего запроса сделать закладку (например, catalog.aspx?category_id=5&group_by=price). В этом случае нужно (удобнее) пользоваться MVC. Ваш код несколько повторяет идею MVC, но реализация получается "всё в одном", когда желательно три составляющих реализовывать отдельно. Но, к сожалению, MVC Framework я ещё не изучил обстоятельно, поэтому "альтернативного" варианта пока от меня не будет...
Вот вы всюду перечисляете old-school и new-school. А что это за термины такие, кем определены, где про них почитать можно?

Контрол олицетворяющий post уже есть и это page.

Про какую модель вы говорите? Про вашу с контролом или про мою? В моем случае закладка никаким боком не выходит из сферы применения. Путь пользователь делает закладки, моему атрибуту все равно.
Event-based модель и MVC (Model-View-Controller) - old-school и new-school соответственно. Я называю MVC new-school'ом только потому, что Microsoft реализовывает свой MVC Framework только сейчас, в 3.5. А до этого только одна модель была доступна по умолчанию.

Чтобы не обращаться к свойству Request, можете посмотреть в msdn'е описание интерфейса IPostBackDataHandler.
То есть вы изъясняетесь на своих собственных терминах, которые сами придумали.
Вопрос по MVC в этом теме вы вообще зря подняли, никаким боком в статье этот шаблон не затрагивается, не упоминается и не используется.

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

Видимо я совершенно не понимаю, что должен делать этот код, который находится в полезных веща 2-3. Поэтому и приходится додумывать, что автор хочет сказать.

Почему я решил, что это каким-то образом похоже на MVC? Потому что используется не модель "по умолчанию", где у каждого элемента управления (TextBox, DropDownList и т.п.) есть свой id-name, который отличен от написаного в дизайнере ID и обработка Request происходит внутри элементов управления (реализации IPostBackDataHandler).

Поскольку не совсем понятно, что у Вас обозначают эти самые Request, и возникает вопрос, вы программируете на ASP.NET или просто используете методы, предоставляемые ASP.NET.
Он должен делать только то, что делает: первый пример проверяет валидность объявленных свойств, второй проецирует строку запроса на свойства, заодно производя небольшую валидацию. первый пример (CheckNull) применим в любом классе C#, второй только в asp.net, посколько если вы внимательно посмотрите, ему передается экземпляр Page.

Я уже спрашивал: "расскажите мне как вы решаете задачу с работой строки запроса?". Как получаете параметры строки, как обрабатываете, как проверяете на наличие? Это тема не этого топика, но раз уж так получилось, ответьте здесь. Мне правда интересно. Я просто удивлен тем, что вы не понимаете, для чего может понадобится биндить параметры строки запроса на свойства страницы.
Так для этого мне и нужно знать - что это за данные, откуда они вводятся и т.п.

Т.е. в проектах, которые я делаю, всё делается на post-back модели, когда пользователь что-то вводит в TextBox, выбирает нужный элемент в DropDownList'е и т.п., нажимает asp:Button и ему выводится соответствующий контент.
:) ну я так подозреваю, что вы не поняли темы данной статьи. В этой статье разбирается ЕДИНСТВЕННО И ТОЛЬКО то как работать со строкой типа bank.aspx?bankId=2&branchId=12&cityId=120
пример призван ТОЛЬКО облегчить работу со значениями этих параметров и ничего более. Причем тут контролы, постбэки и то как чтото вводит пользователь? Страница - это черный ящик у нее на входе кроме всего прочего только строка url. Такая строка может быть образована в списке пунктов обмена для города у конкретного банка и сформирована обычными элементами Hyperlink или еще в миллионе случаев. Пример с биндингом строки запроса решает проблему определения в вашей бизнес-модели входных параметров строки запроса Url страницы.
Я заметил некоторого рода стратегическую ошибку в том коде: использование Request[], вместо Request.QueryString[]. Наверное из-за этого возникло какое-то непонимание. Код исправил.
К стати, эти приложения в основном для интранета? Ну или для использования в каких-то бэкофисах. Или я ошибся?
Пропустил: валидаторы - это которые контролы или что-то другое? Дело в том, что данный механизм применим не только в asp.net.

Все приватные поля страницы не будут видны если взять тип через this.GetType().

И это хорошо. Нечего на них извне смотреть.
Хорошо согласен, но в моем случае из-за этого приходится исхитряться. А как еще проверить атрибут у private поля или свойства?
Есть такая методология, которая называется "проектирование по контракту". Суть ее заключается в том, что между вызываемым кодом и исполняемым заключается некий контракт. Ну например в контракте говорится: для того что бы вызвать метод А, необходимо что бы параметр П не был равен null, в таком случае метод вернет положительное число. Здесь применяется два утверждения: первое - это предусловие, выполнение которого необходимо для коректного выполнения метода, второе - это постусловие, утверждение которое должно выполняться после выполнения метода, если все предусловия были выполнены со стороны вызываемого кода.
Кроме того, в теории есть так называемые "инварианты" - это такое утверждение, которое должно выполняться во время всей жизни объекта. В частности это обычно применяется для свойств, т.е. в вашем случае CheckNull представляет собой как раз инвариант "Свойство BranchId должно быть инициализированно".
Для платформы .NET есть несколько библиотек, которые пытаются как раз внедрить методологию "проектирования по контракту" для практического использования - ну например eXtensible C#, но у всех у них есть довольно большой набор недостатков (теория проектирования по контракту так и остается из-за этого по большей части только теорией).
Ну все это было по большей части приамбулой ;) Сейчас я работаю над созданием такого рода библиотеки, только которая бы решала ряд недостатков других и была бы более гибкой в использовании (работа кипит, но никак не закипает ;). При работе над ней я сталкивался с рядом спорных вопросов, один из которых в частности обсуждаются и здесь в комментариях. Касается он возбуждаемого исключения - что делать если утверждение не выполняется, генерировать исключение или возвращать список ошибок или что то еще?
На самом деле есть несколько вариантов:
Первый - сгенерировать просто исключение. Плох тем, что нельзя понять причину исключения (без просмотра сообщения человеком, конечно), так же, возбуждение исключения - это довольно дорогая операция.
Второй способ - генерировать специфичное исключение, ну например при привязке к теории "проектирования по контракту" можно быделить три типа таких исключений RequireException (не выполняется предусловие для вызова метода), EnsureException (не выполняется постусловие), InvariantException (не выполняется инвариант). Отличается от первого способа тем, что по типу исключения сразу становится ясно какого рода исключение случилось (полезно при отладке).
Третий способ - при невыполнении утверждения вызвать метод, который поправит ситуацию. Ну в данном случае возможно два подхода - проинициализировать BranchId (например подставить последний используемый для данного пользователя), или перебросить пользователя на другую страницу (например список всех отделений). Способ хорош тем, что никакого исключения не генерируется. Так же в нем можно увидеть программирование некоторых use case`ов: "если пользовател сразу открыл страницу просмотра какого то отделения, но отделение он перед эти не выбирал (в рамках текущей сессии, ест-но), то открыть ему отделение которое он просматривал последний раз", или: "если пользователь сразу открыл страницу просмотра какого то отделения, но отделения он перед этим не выбирал, то предложить ему список отделений".
Четвертый способ - возвратить список ошибок. Недостаток в том, что это еще потом придется обрабатывать как то. Ну и вообще, для инвариантов этот способ обычно не подходит ;)
P.S. Какой то длинный получился комментарий, надеюсь хотя бы немного он будет полезным ;)
P.P.S. Я бы на вашем месте выбрал третий способ обработки. А какой из двух подвариантов - уже зависит от ситуации ;)
Спасибо за отличный комментарий!
По поводу третьего варианта: на самом деле в моем случае он как раз таки и не подходит. Задача стоит проверить валидность некоего свойства, логика обработки исключения должна уже исходить из контекста. Например вызов CheckNullMembers.Check(this.GetType().BaseType, this) можно заключить в try catch и поступать по ситуации. Считаю, что сама же проверка (Check) обязана выдавать исключение.

Кстати, по поводу исключений, я вчера специально почитал, что думает Рихтер и думает он так же как и я - все делать через исключения. Другое дело, что конечно же стоит отдавать предпочтение своим исключениям, но для моего примера объявлять еще и исключения - это лишнее, думаю.
Ну да, скорее всего :) Все таки тут проверка именно аттрибута CheckNull.
Исключение говорит о том, что в выполняемом коде что то случилось не так. Рихтер скорее всего имел в виду то, что лучше генерировать исключение, а не возвращать что то типа null, по которому нельзя понять, что в методе случилось ошибка.
В данном случае это проверящий метод, который говорит - случилось что нибудь или нет, так что быть может подойдет подход и не только с исключением.
В целом согласен.
Рихтер, кстати говорит одну хорошую мысль: исключение в коде не означает только ошибку.
Только полноправные пользователи могут оставлять комментарии. , пожалуйста.