Потому что так исторически сложилось.
1. Очень многие механизмы раньше (а какие-то и сейчас) работают только со свойствами, но не с полями.
А если они и умеют работать с полями - часто по-умолчанию они с полями не работают.
2. Даже если ты делаешь автосвойство - ты всё равно можешь делать с ним всё то же самое, что и с обычным свойством: вынести его в интерфейс, сделать виртуальным и переопределить в наследниках, а потом обратиться к нему не на конкретном типе, а на интерфейсе. Это может быть очень полезно, а иногда и необходимо в некоторых случаях
3. Если ты разрабатываешь какую-то библиотеку, которую будет использовать кто-то ещё, то ты скорее всего хочешь как можно меньше ломающих изменений в новых версиях - тогда имеет смысл превентивно сделать свойство, чтобы потом не переделывать.
По сути всё.
Сказки про инкапсуляцию оставим для учебников, так как если мы делаем какую-то тупую DTO-шку, то никакой пользы от обращения через свойства мы не получим.
Накладных расходов у свойств по сути нет, так как JIT их заинлайнит.
А раз никаких минусов нет - зачем включать мозг и думать "
а понадобится ли мне по какой-то причине тут свойство или можно обойтись полем"? А потом ещё огребать, если ошибся (даже если это редкий случай)
Единообразие тут скорее благо.
к примеру, не сделать этот int отрицательным
На самом деле это очень редкий кейс.
Зачем делать какую-то валидацию, если можно изначально использовать тип, который не допускает отрицательных значений?
но вот смысл автоматических свойств я так и не могу осознать, хоть убейте
Смысл автосвойств - чтобы не писать руками { get {return x;} set {x = value;}}.
А смысл свойств вообще - чтобы можно было вынести в интерфейс, переопределить, итд.
А ещё у свойства я могу не писать set или вместо set написать init и required, чего я не смогу сделать в классе с полями.
Да, у поля можно написать readonly и получить по сути то же самое, но тогда его надо будет обязательно через конструктор инициализировать.