Я добавляю этот ответ, чтобы дополнить ответ Роджера Алсинга об инвариантах другими причинами.
Семантическая информация
Роджер четко объяснил, что установщики свойств не несут семантической информации. Разрешение установки для свойства, такого как Post.PublishDate, может привести к путанице, поскольку мы не можем точно знать, была ли публикация опубликована или только если дата публикации была установлена. Мы не можем знать наверняка, что это все, что нужно для публикации статьи. «Интерфейс» объекта не показывает свое «намерение».
Тем не менее, я считаю, что такие свойства, как «Включено», несут достаточно семантики для «получения» и «установки». Это то, что должно вступить в силу немедленно, и я не вижу необходимости в методах SetActive () или Activate () / Deactivate () по одной только причине отсутствия семантики в установщике свойств.
Инварианты
Роджер также говорил о возможности взлома инвариантов через установщики свойств. Это абсолютно правильно, и нужно создать свойства, которые работают в тандеме, чтобы обеспечить «объединенное инвариантное значение» (в качестве углов треугольника, чтобы использовать пример Роджера) в качестве свойств только для чтения, и создать метод, чтобы установить их все вместе, что может проверить все комбинации за один шаг.
Инициализация / установка порядка свойств зависимостей
Это похоже на проблему с инвариантами, поскольку вызывает проблемы со свойствами, которые должны проверяться / изменяться вместе. Представьте себе следующий код:
public class Period
{
DateTime start;
public DateTime Start
{
get { return start; }
set
{
if (value > end && end != default(DateTime))
throw new Exception("Start can't be later than end!");
start = value;
}
}
DateTime end;
public DateTime End
{
get { return end; }
set
{
if (value < start && start != default(DateTime))
throw new Exception("End can't be earlier than start!");
end = value;
}
}
}
Это наивный пример проверки «сеттера», которая вызывает зависимости порядка доступа. Следующий код иллюстрирует эту проблему:
public void CanChangeStartAndEndInAnyOrder()
{
Period period = new Period(DateTime.Now, DateTime.Now);
period.Start = DateTime.Now.AddDays(1); //--> will throw exception here
period.End = DateTime.Now.AddDays(2);
// the following may throw an exception depending on the order the C# compiler
// assigns the properties.
period = new Period()
{
Start = DateTime.Now.AddDays(1),
End = DateTime.Now.AddDays(2),
};
// The order is not guaranteed by C#, so either way may throw an exception
period = new Period()
{
End = DateTime.Now.AddDays(2),
Start = DateTime.Now.AddDays(1),
};
}
Поскольку мы не можем изменить дату начала после даты окончания в объекте периода (если только это не «пустой» период, когда обе даты установлены по умолчанию (DateTime) - да, это не очень хороший дизайн, но вы поймите, что я имею в виду ...) сначала попытка установить дату начала вызовет исключение.
Это становится более серьезным, когда мы используем инициализаторы объектов. Поскольку C # не гарантирует какой-либо порядок присваивания, мы не можем делать безопасных предположений, и код может выдавать или не выдавать исключение в зависимости от выбора компилятора. BAD!
В конечном итоге это проблема ПРОЕКТИРОВАНИЯ классов. Поскольку свойство не может «знать», что вы обновляете оба значения, оно не может «отключить» проверку, пока оба значения не будут фактически изменены. Вы должны либо сделать оба свойства доступными только для чтения и предоставить метод для одновременной установки обоих (теряя особенность инициализаторов объектов), либо вообще удалить код проверки из свойств (возможно, вводя другое свойство только для чтения, такое как IsValid, или выполнить валидацию). это когда нужно).
ОРМ "Увлажнение" *
Гидратация, в упрощенном виде, означает возвращение постоянных данных обратно в объекты. Для меня это действительно самая большая проблема с добавлением логики за установщиками свойств.
Многие / большинство ORM отображают постоянное значение в свойство. Если у вас есть логика проверки или логика, которая изменяет состояние объекта (других членов) внутри установщиков свойств, вы в конечном итоге попытаетесь выполнить проверку на предмет «неполного» объекта (который все еще загружается). Это очень похоже на проблему инициализации объекта, так как вы не можете контролировать порядок «полей» полей.
Большинство ORM позволяют вам сопоставлять персистентность с закрытыми полями вместо свойств, и это позволит гидратировать объекты, но если ваша логика проверки находится в основном внутри установщиков свойств, вам, возможно, придется дублировать ее в другом месте, чтобы проверить, что загруженный объект объект действителен или нет.
Поскольку многие инструменты ORM поддерживают отложенную загрузку (фундаментальный аспект ORM!) Посредством использования виртуальных свойств (или методов), сопоставление с полями сделает невозможным для ORM отложенную загрузку объектов, сопоставленных с полями.
Заключение
Итак, в конце, чтобы избежать дублирования кода, позволить ORM работать как можно лучше, не допускать неожиданных исключений в зависимости от порядка, в котором заданы поля, разумно убрать логику из установщиков свойств.
Я все еще выясняю, где должна быть эта логика «проверки». Где мы проверяем аспекты инвариантов объекта? Куда мы помещаем проверки более высокого уровня? Используем ли мы хуки на ORM для выполнения валидации (OnSave, OnDelete, ...)? И т. Д. И т. Д. Но это не является предметом этого ответа.