Использование явных интерфейсов для предотвращения случайного изменения свойств в C#
-
19-09-2019 - |
Вопрос
Я наткнулся на особенность разрешения методов C#, которую раньше не замечал.А именно, когда я явно реализую интерфейс, поддерживающий метод установки, а неявный интерфейс предлагает только защищенный набор, компилятор разумно подчиняется защищенному набору, когда я его вызываю.Таким образом, я получаю большую часть удобства автоматически реализуемых свойств, но могу предотвратить случайное изменение полей клиентами, которым не следует их изменять.
В качестве примера,
virtual public DateTime CreatedOn { get; protected set; }
virtual public DateTime? ModifiedOn { get; protected set; }
#region IHaveUpdateDateFields Members
DateTime IHaveUpdateDateFields.CreatedOn
{
get
{
return this.CreatedOn;
}
set
{
this.CreatedOn = value;
}
}
DateTime? IHaveUpdateDateFields.ModifiedOn
{
get
{
return this.ModifiedOn;
}
set
{
this.ModifiedOn = value;
}
}
Тогда мой код привязки модели не устанавливает дату случайно, но мой прослушиватель событий ORM может проверять объекты, реализующие IHaveUpdateDateFields, и устанавливать дату при сохранении моей сущности.
Мои вопросы:
- Полагаюсь ли я на определенное поведение или мне гарантировано, что все компиляторы C# будут разрешать методы таким образом?Я не хочу обнаружить, что стандарт C# гласит, что такое разрешение метода не определено, а затем случайно получить ужасное переполнение стека, например, при сборке для Mono.
- Есть ли более приятный (в идеале более краткий) способ сделать это?Я мог бы иметь интерфейс, безопасный для ModelBinder, который я передаю своему контроллеру, но не похоже, что это сэкономит мне код, и я не думаю, что он обеспечит столь же прозрачный подход к минимизации случайного изменения свойств.
Решение
Это совершенно четко определено;явные реализации интерфейса имеют приоритет при использовании интерфейса, а обычное свойство вступает в силу в противном случае (в том числе внутри тела get/set).
А насчет того, чтобы сделать его более аккуратным...лучшее, что я могу предложить, это переформатировать его, чтобы сделать его менее многословным...
DateTime IHaveUpdateDateFields.CreatedOn
{
get { return CreatedOn; }
set { CreatedOn = value; }
}
(обратите также внимание на то, что this
является неявным и избыточным)
Кстати, безопасность – это всего лишь удобство, а не гарантия...внешние вызывающие абоненты по-прежнему могут использовать ваш интерфейс и обычно могут (ab) использовать отражение, чтобы перепрыгнуть через такие простые вещи, как protected
- или даже просто установите поля напрямую.