Pull to refresh

Comments 37

Я использую инварианты контрактов для таких вещей, если поля относятся напрямую к классу и не представляют отдельных сущностей.
Если же поле сильно специфично и имеет своё какое-никакое состояние — выделяю в отдельный класс.
Но тут тогда надо ставить хуки на коммит, чтобы при warning'е от статической прогонки контрактов код не коммитился.
С code contract знаком поверхностно. Возможно, инварианты и решили бы проблему. Погляжу, оценю.
Они не решили бы проблему на уровне компиляции (нет проблем что-то сломать), но при нарушении условий всегда будет вываливаться ворнинг после статической проверки контрактов, а на это уже вешаются хуки. Если в команде разработчиков нет саботеров, то контракты — красивое решение.
Вот тут не согласен, т.к. когда посыпется проект, начальство свечи, которых игра не стоила, применит по прямому назначению.
Метод выбран не очень удачно ИМХО, но проблему целостности (integrity) класса надо решать.
Вполне возможно. Тут вопросов встречных два: а) Как иным образом решить задачу? б) Разве для некоторых проектов предложенное решение не является приемлимым и полезным? Я не утверждаю, что решение идеально. Мне интересен взгляд людей со стороны.
а) Да просто запомнить, что и внутри и вне класса надо обращаться у св-ву, а не полю. Честно, не вижу в этом проблемы. Разве я не прав? Ведь это не так сложно.

б) Приемлемым? А есть выбор? Решение… хмм… ну, скажем так, стандартное. я так думаю
Мне не нравится решение в стиле «запомнить». Это легко забыть. И если вы (я или Вася Муркин) вдруг таки забыл, что есть свойство и будет использовать поле, компилятор не скажет ему ай-яй-яй. Кстати, отсутствие подобного рода проверок — единственное, что мне не нравится в интерпретируемых языках.

Пункт б) я перечитал несколько раз и не понял его, простите.
Видимо просто люди мы такие) Разные.

Насчет пункта б, я хотел сказать, что решение я считаю приемлемым, но, наверное, только потому, что другого решения я не могу даже представить.
Да, все люди разные. Отчасти я стараюсь как можно больше полагаться компилятор, из-за своей плохой памяти. В любом случае, спасибо за оценку. Мнение со стороны полезно.
По поводу стиля кода — зря не привередничаете, когда все выдержано в одном стиле и легко читается — экономите кучу времени.
Честно говоря не вижу смысла.
Если очень надо, можно именовать backing field как что-то непонятное (ну вы понели), например ___backingField1.
К сожалению, именование не защищает никак от новых людей в проекте и от забывчивости.
Вы наверное меня неправильно поняли. Вот пример:

private bool ___backingField1;
private double ___backingField2;

public bool IsFoo
{
get {return ___backingField1;}
set {___backingField1 = value;}
}

public double MaxSpeed
{
get { return ___backingField2; }
set
{
if (value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED)
throw new ArgumentOutOfRangeException(«MaxSpeed»);
___backingField2 = value;
}
}

И так далее. Смысл в том, что надо именовать backing field'ы так, чтобы разработчик не понял, к какому свойству оно относится. Я не думаю, что кто-то полезет смотреть свойство (ну если только совсем упоротые).
Да, я сперва совсем не так понял. Спасибо за разъяснение.
Интересный вариант. С ходу из недостатков вижу только намеренно бессмысленное именование, которое планируется использовать как достоинство. Ну и нет, к сожалению, защиты от того, что кто-то всё-таки напишет по ошибке ___backingField2 = someWrongValue; Прямой доступ всё-таки останется. Хотя в хорошей, дисциплинированной команде такая ситуация практически исключена.
Ни в коем случае так нельзя делать!
Честно — этот вопрос поставил меня в ступор, это почти как «ходить» или «нос» — само собой разумеющееся.
Это порождает тонну лишнего кода + легко ошибиться в самих свойствах с именами переменных, ну и вообще подход какой-то странный, нарушает все конвенции именования.
А чтоб не так?
[Obsolete("Do not use directly", false)]
private int _MyProperty;

#pragma warning disable 0618
public int MyProperty { get { return _MyProperty; } set { _MyProperty = value; } }
#pragma warning restore 0618

void foo()
{
_MyProperty = 10; //bad code!
}

(vs.net) + Threat warnings as errors — All?
Во-первых, о таком даже не подумал. Во-вторых, я не очень люблю игры с директивами, честно говоря. Да и threat warnings as errors не всегда, подходят, хотя я сам двумя руками ЗА такую практику.
В остальном же, решение подкупает своей простотой. Довольно изящно.
Ну и поле на самом деле не Obsolete, конечно. :)
Оригинально! Кстати говоря, решарпер, кажется, будет зачеркивать все что помечено данным аттрибутом.
С другой стороны разработчики, незнающие будут внедоумении, почему поле устарело :)
У меня это выглядело бы так:

public double MaxSpeed { get; set; }

и пусть туда кто хочет и что хочет пишет. А при сохранении в сервисе который занимается сохранением отдельный валидатор который проверит все что нужно и не пропустит дальше если там что то не так.
Этого часто недостаточно. Одно дело, когда есть такой сервис, который сохраняет и имеет валидатор. И совсем другое, когда объект сконструирован и может/должен быть сразу применён согласно какой-то логике. Мне очень нравится, когда есть возможность иметь какие-то возможности «автоматически», без лишних телодвижений.

Как я уже сказал, в идеале полное сокрытие должно быть реализовано в самом языке C#.
>Как я уже сказал, в идеале полное сокрытие должно быть реализовано в самом языке C#.
Было бы прекрасно такое увидеть! В целом, не вижу никаких препятствий на пути этому.

Кстати говоря сокрытые, условно говоря, реализовано в свойствах зависимости. Но увы, это как «из пушки по воробьям палить».
Давайте посмотрим с другой стороны:
Если метод внутри класса обращается к полю и может испортить в нем данные — это значит что этот метод и это поле не лежат на одном уровне абстракции, а значит — why on this Earth оба они находятся в одном классе? Надо или делать базовый класс со свойствами, или выделять отдельные классы, или как либо иначе пересматривать архитектуру класса.
Не спорю. Этот момент я описал в статье как тру-ООП подход.
Ну а зачем тогда плодить не «тру-ООП» код? Экономия времени здесь только мнимая, в результате при нарушении принципов ООП в угоду количеству строк кода сложность разработки только увеличится.
По сути, мой подход тоже тру-ООП. HidingProperty это фактически мини-класс, берущий на себя ответственность за правила обращения к полю. Если эти правила тривиальны как в описанном случае, то я не вижу большого прироста сложности. Таким образом, HidingProperty инкапсулирует микрослой знаний о типе данных Speed в данном примере. Раз уж из всех особенностей этого типа мы имеем только ограничение на геттер и сеттер, то чем плохо декларировать этот тип по месту его единственного использования? Если бы ещё и синтаксис был таким как его предложил мой коллега, то всё вообще выглядело совсем естественно. Поправьте, если не так.
Этот мини класс заставляет завязываться на себя весь код: код внутри класса, код внутри вашей системы, код клиента. И если что-то поменялось — можно огрести кучу проблем себе на голову.
При этом абсолютно не понятна экономия, т.к. по сути экономятся здесь спички.
Ещё раз повторю — что это задача соблюдения инварианта, и она решается статически уже созданными средствами.
Если имеется в виду противный интерфейс с мерзкими Get(), Set(), то я согласен. В противном случае (поддержка на уровне языка), всё было бы абсолютно прозрачно.
Но поддержки на уровне языка нет, можно конечно обернуть это еще сверху в свойства, но все равно это будет костыль.
Да, костыль и очень уж неприятный. Пробовал реализовать неявное приведение, но это удалось сделать только в одну сторону (из HidingProperty в T). Да даже если реализовал бы, это было бы не очень хорошо. У implicit cast операторов есть свои проблемы с использованием, насколько я знаю.

Так может тогда есть смысл закрыть тему, чтобы люди не читали моей ереси?
Sign up to leave a comment.

Articles