Какую роль играет value в сеттерах свойств

Какую роль играет value в сеттерах свойств thumbnail

Понятие дескриптора свойства объекта

Объект и его свойства (поля)

Объект — это набор свойств и их значений в памяти, на которые можно сослаться с помощью идентификатора.

// Объект user

const user = {

  firstName: “Alex”,

  lastName: “NAV”,

  “user-adress”: {  

    country: “Russia”,

    sity: “Moscow”

  },

};

Свойство объекта – это пара «ключ: значение» (или key: value):

  • ключ — это идентификатор (или имя) свойства (тип String или Symbol),
  • значения могут быть любыми (любой тип, включая другие объекты).

Свойства объекта также иногда называют полями объекта.

Бесплатный курс по JavaScript

Значения свойств могут иметь любой тип, включая другие объекты, что позволяет строить сложные, разветвлённые иерархии данных.

[свернуть]

Дескриптор (флаг) — это объект, позволяющий описать поведение свойства при выполнении определённых операций над ним (например, чтения или записи).

Дескрипторы свойств, присутствующие в объектах, бывают двух основных типов:

  1. дескрипторы данных (для свойства, имеющего значение, которое может (или не может) быть записываемым);
  2. дескрипторы доступа (свойство, описываемое парой функций — геттером (get) и сеттером (set).

Дескриптор может быть только одним из этих двух типов; он не может быть одновременно обоими.

Дескрипторы свойств могут содержать следующие ключи:

  1. value — значение, ассоциированное со свойством; может быть любым допустимым значением JavaScript (числом, объектом, функцией и т.д.) (по умолчанию  undefined);
  2. writable — равен true только в том случае, если значение, ассоциированное со свойством, может быть изменено с помощью оператора присваивания (по умолчанию установлено в false — перезаписать значение нельзя);
  3. enumerable — равен true только в том случае, если это свойство доступно через перечисление свойств содержащего его объекта (по умолчанию установлено в false; при false свойство будет отсутствовать в перечне перечисляемых свойств объекта);
  4. configurable — равен true только в том случае, если тип свойства может быть изменен и если свойство может быть удалено из содержащего его объекта (по умолчанию — false);
  5. get — функция, используемая как геттер свойства, либо undefined, если свойство не имеет геттера; возвращаемое значение функции будет использоваться как значение свойства (по умолчанию undefined);
  6. set — функция, используемая как сеттер свойства, либо undefined, если свойство не имеет сеттера; функция принимает единственным аргументом новое значение, присваиваемое свойству (по умолчанию установлено в undefined).

ВАЖНО! Чтобы избежать конфликтов запрещено одновременно указывать значение value и функции get/set  (указывают либо значение (value), либо функции для его чтения-записи (get или set)). Также запрещено и не имеет смысла указывать writable при наличии get/set-функций.

Примеры использования дескрипторов

Дескриптор Writable:

const car = {};

Object.defineProperty(car, “brand”, {

  value: “Nissan”,

  writable: false // только для чтения, перезаписать (изменить) значение свойства нельзя

});

car.brand = “Opel”; // TypeError: “brand” is read-only (ошибка)

Дескриптор Configurable:

Дескриптор configurable определяет:

  1. можно ли изменить тип свойства enumerable или configurable с помощью функции Object.defineProperty();
  2. может ли свойство  быть удалено из содержащего его объекта.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

const car = {};

Object.defineProperty(car, “brand”, {

  value: “Nissan”,

  writable: true,

  configurable: false // запрет на изменение и удаление свойства объекта

});

// Пытаемся изменить значения дескрипторов enumerable (или configurable),

// ошибка TypeError: can’t redefine non-configurable property “brand”

Object.defineProperty(car, “brand”, {

  value: “BMW”,

  writable: false,

  configurable: false,

  enumerable: true

});

// Пытаемся удалить свойство “brand”,

// ошибка TypeError: property “brand” is non-configurable and can’t be deleted

delete car.brand;

Значение дескриптора value будет перезаписано только в том случае, если значения для остальных дескрипторов полностью совпадают с предыдущим присваиванием:

const car = {};

Object.defineProperty(car, “brand”, {

  value: “Nissan”,

  writable: true,

  configurable: false

});

Object.defineProperty(car, “brand”, {

  value: “BMW”,

  writable: false,

  configurable: false,

  enumerable: false,

});

res = car.brand;

console.log(res); // BMW (ошибки не будет, значение value изменится)

Дескриптор Enumerable:

Дескриптор enumerable определяет:

  1. будет ли свойство перебираться с помощью цикла for .. in;
  2. можно ли получить название свойства с помощью функции Object.keys.

const car = {};

Object.defineProperties(car, {

  brand: {

    value: “BMW”,

    enumerable: true

  },

  age: {

    value: 10,

    enumerable: false

  }

});

res = Object.keys(car);

console.log(res); // Array [ “brand” ] (свойство age отсутствует)

[свернуть]

Использование дескрипторов при работе со свойствами объекта

Получение значений дескрипторов для свойств объекта:

Получение дескрипторов одного свойства ( метод Object.getOwnPropertyDescriptor() )

Метод Object.getOwnPropertyDescriptor() возвращает дескриптор собственного свойства  объекта, переданного в метод (собственное свойство объекта находится непосредственно в объекте, а не получено через цепочку прототипов.

Object.getOwnPropertyDescriptor( obj, prop )

где obj — объект, в котором ищется свойство; prop — имя свойства, чьё описание (дескрипторы) будет возвращено.

Метод Object.getOwnPropertyDescriptor()  возвращает дескриптор переданного свойства, если оно присутствует в объекте, либо undefined, если его там нет.

[свернуть]

Получение дескрипторов всех свойств объекта ( метод Object.getOwnPropertyDescriptors() )

Метод Object.getOwnPropertyDescriptors() возвращает все собственные дескрипторы свойств переданного объекта.

Object.getOwnPropertyDescriptors( obj )

где obj — объект, для которого нужно получить все собственные дескрипторы свойств.

Метод Object.getOwnPropertyDescriptors() возвращает объект, содержащий все собственные дескрипторы свойств объекта. Если у переданного объекта нет свойств, то возвращается пустой объект.

[свернуть]

Примеры

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

let user = {

  name: “Alex”,

  surname: “NAV”,

  age: 40,

  skills: [“teacher”, “JavaScript”]

};

// Получение значений дескрипторов для конкретного свойства объекта

let res = Object.getOwnPropertyDescriptor(user, ‘name’);

console.log(res); // Object { value: “Alex”, writable: true, enumerable: true, configurable: true }

// Получение значений дескрипторов для всех свойств объекта

res = Object.getOwnPropertyDescriptors(user);

console.log(res);

// {…}​

// age: Object { value: 40, writable: true, enumerable: true, … }​

// name: Object { value: “Alex”, writable: true, enumerable: true, … }​

// skills: Object { writable: true, enumerable: true, configurable: true, … }​

// surname: Object { value: “NAV”, writable: true, enumerable: true, … }

// Если передан пустой объект (без свойств)

user = {};

res = Object.getOwnPropertyDescriptors(user);

console.log(res); // Object {  }

[свернуть]

Добавление (изменение) свойств объекта:

Добавление (изменение) одного свойства объекта ( метод Object.defineProperty() )

Метод Object.defineProperties() определяет (добавляет) новые или изменяет существующие свойства, непосредственно в объекте, возвращая этот объект.

При вызове метода учитывается значение дескриптора configurable!

Object.defineProperties( obj, props )

где obj — объект, в котором определяются новые или изменяются существующие свойства; props — объект, чьи собственные перечисляемые свойства представляют собой дескрипторы для создаваемых или изменяемых свойств.

Читайте также:  Какими химическими свойствами обладают галогены

Метод возвращает измененный объект.

const user = {};

Object.defineProperty(user, ‘name’, {

  value: “Olga”,

  enumerable: true

});

console.log(user); // Object { name: “Olga” }

res = Object.getOwnPropertyDescriptors(user);

console.log(res);

// name: Object

// { configurable: false

// enumerable: true​​

// value: “Olga”​​

// writable: false }

Обратите внимание на значения по умолчанию дескрипторов configurable: false и  writable: false.

[свернуть]

Добавление (изменение) нескольких свойств объекта ( метод Object.defineProperties() )

const user = {};

Object.defineProperties(user, {

  name: {

    value: “Olga”,

    enumerable: true

  },

  age: {

    value: 25,

    configurable: true,

    enumerable: true,

    writable: true

  }

});

console.log(user); // Object { name: “Olga”, age: 25 }

Метод Object.defineProperties() позволяет определять все свойства переданного объекта.

При вызове метода учитывается значение дескриптора configurable!

[свернуть]

Геттеры (get) и сеттеры (set)

Дескрипторы get и set (более известные как геттеры и сеттеры) позволяют задавать функции в качестве свойств объекта, а также запускать их при запросе к вызову или записи свойства соответственно:

  • get – функция, которая возвращает значение свойства (по умолчанию undefined);
    set – функция, которая записывает значение свойства (по умолчанию undefined).

const myObj = {

  get getter() { // геттер, срабатывает при чтении свойства obj.getter

  },

  set setter(value) { // сеттер, срабатывает при записи свойства obj.setter = value    

  }

};

Указание get и set в в качестве свойства в литерале объекта

Если мы создаём объект при помощи литерального синтаксиса { … }, то задать свойства-функции можно прямо в его определении.

let myCar = {

  brand: “Opel”,

  year: 2014,

  set age(value) {

    this.year = 2020 – value;

  },

  get allInfo() {

    return `Car: ${this.brand}, year: ${this.year}`;

  }

};

myCar.age = 2010; // вызываем сеттер и передаем значение

console.log(myCar.allInfo); // Car: Opel, year: 10 (вызов геттера)

Геттер срабатывает, когда myCar.allInfo вызывается (читается), сеттер – когда значение myCar.age присваивается.

При этом мы не вызываем геттер (например, myCar.allInfo) или сеттер как функцию, а просто читаем его значение (геттер или сеттер выполняются скрыто). Снаружи свойство-аксессор выглядит как обычное свойство объекта.

Добавление get или set в существующий объект

С помощью методов Object.defineProperty() и Object.defineProperties() можно добавить get или set в уже существующий объект.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// Исходный объект

let myCar = {

  brand: “Opel”,

  year: 0

};

Object.defineProperty(myCar, “age”, {

  set: function(value) {

    this.year = 2020 – value;

  },

  get: function() {

    return `Car: ${this.brand}, year: ${this.year}`;

  }

});

myCar.age = 2010; // вызываем сеттер и передаем значение

console.log(myCar.age); // Car: Opel, year: 10 (вызов геттера)

console.log(myCar); // Object { brand: “Opel”, year: 10, … }

Ещё раз отметим, что свойство объекта может быть либо свойством-данным (со значением value), либо свойством-аксессором (с методами get/set).

ВАЖНО! Попытка указать и get/set, и value в одном дескрипторе вызовет ошибку:

Object.defineProperty(myCar, “age”, {

  value: 0, // TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified

  set: function(value) {

    this.year = 2020 – value;

  },

});

Использование get или set в качестве обёртки

Геттеры/сеттеры можно использовать как обёртки над «реальными» значениями свойств, чтобы получить больше контроля над операциями с ними.

Например, если мы хотим запретить устанавливать короткое имя для login, мы можем использовать сеттер login для проверки, а само значение хранить в отдельном внутреннем свойстве _login:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

let userLogin = {

  get login() {

    return this._login;

  },

  set login(value) {

    if (value.length < 5) {

      console.log(“Login is short”);

    } else {

      return (this._login = value);

    }

  }

};

userLogin.login = “Adam”; // Login is short

console.log(userLogin.login) // undefined

console.log(userLogin) // Object { login: Getter & Setter }

userLogin.login = “AdamSmith”;

console.log(userLogin.login) // AdamSmith

console.log(userLogin) // Object { login: Getter & Setter, _login: “AdamSmith” }

Таким образом, login хранится в свойстве _login, доступ к которому производится через геттер и сеттер.

Замечание: технически можно получить доступ к login напрямую с помощью userLogin._login, но существует соглашение о том что свойства, которые начинаются с символа «_», являются внутренними, и к ним не следует обращаться из-за пределов объекта.

Использование get или set для совместимости кода

Аксессоры позволяют в любой момент взять «обычное» свойство и изменить его поведение, поменяв на геттер и сеттер.

Пример отсюда

Например, представим, что мы начали реализовывать объект user, используя свойства-данные имя name и возраст age:

function User(name, age) {

  this.name = name;

  this.age = age;

}

let john = new User(“John”, 25);

console.log(`Возраст: ${john.age}`); // Возраст: 25

Но вместо возраста age мы можем решить хранить дату рождения birthday, потому что так более точно и удобно:

function User(name, birthday) {

  this.name = name;

  this.birthday = birthday;

}

let john = new User(“John”, new Date(1992, 6, 1));

console.log(`Дата рождения: ${john.birthday}`); // Дата рождения: Wed Jul 01 1992 00:00:00 GMT+0600 (Екатеринбург, летнее время)

Что делать со старым кодом, который использует свойство age?

Мы можем попытаться найти все такие места и изменить их, но это отнимает время и может быть невыполнимо, если код используется другими людьми. И кроме того, age может использоваться и в других местах кода. Давайте его сохраним.

Добавление геттера для age решит проблему:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

function User(name, birthday) {

  this.name = name;

  this.birthday = birthday;

// Возраст рассчитывается из текущей даты и дня рождения

  Object.defineProperty(this, “age”, {

    get() {

      let todayYear = new Date().getFullYear();

      return todayYear – this.birthday.getFullYear();

    }

  });

}

let john = new User(“John”, new Date(1992, 6, 1));

console.log(john.birthday); // доступен как день рождения

console.log(john.age); // …так и возраст

Теперь старый код тоже работает, и у нас есть дополнительное свойство age.

Источник

Javascript — очень изящный язык с кучей интересных возможностей. Большинство из этих возможностей скрыты одним неприятным фактором — Internet Explorer’ом и другим дерьмом, с которым нам приходится работать. Тем не менее, с приходом мобильных телефонов с актуальными браузерами и серверного JavaScript с нормальными движками эти возможности уже можно и нужно использовать прям сейчас. Но по привычке, даже при программировании для node.js мы стараемся писать так, чтобы оно работало в IE6+.

Читайте также:  По каким свойствам серые чугуны выгодно отличаются от углеродистых сталей

В этой статье я расскажу про интересный и не секретный способ указывать изящные геттеры и сеттеры и немножко покопаемся в исходниках Mootools. Частично это информация взята из статьи John Resig, частично лично мой опыт и эксперименты.

function Foo(bar){
this._bar = bar;
}

Foo.prototype = {
get bar () {
return this._bar;
},
set bar (bar) {
this._bar = bar;
}
};

Стандартные геттеры

Что такое Геттеры и Сеттеры, надеюсь знают все. Обычно и кроссбраузерно это выглядит так:

function Foo(bar) {
this._bar = bar;
};
Foo.prototype = {
setBar : function (bar) {
this._bar = bar;
},
getBar : function () {
return this._bar;
}
};
var foo = new Foo;
foo.setBar(123);
alert(foo.getBar());

Можно пойти дальше и написать более изящный вариант:

function Foo(bar) {
var _bar = bar;

this.bar = function (bar) {
if (arguments.length)
_bar = bar;
else
return _bar;
}
};
var foo = new Foo;
foo.bar(123);
alert(foo.bar());

Нативные геттеры/сеттеры

Но есть более удобный способ, который работает во всех серверных движках и современных браузерах, а именно Firefox, Chrome, Safari3+, Opera9.5+ — задание сеттера и геттера для свойства так, чтобы продолжать обращатся к свойству, как свойству. У такого подхода есть несколько преимуществ:
1. Более изящная запись. Представим ORM:

for (var i in topics.comments);
// vs
for (var i in topics.loadComments());

2. Если апи, которое базируется на свойствах уже есть и его нельзя менять (а очень нужно).

Есть два способа задать такой геттер/сеттер:

Через объект:

function Foo(bar) {
this._bar = bar;
};
Foo.prototype = {
set bar (bar) {
this._bar = bar;
},
get bar () {
return this._bar;
}
};
var foo = new Foo;
foo.bar = 123;
alert(foo.bar);

Через методы __defineGetter__ и __defineSetter__:

function Foo(bar) {
var _bar = bar;

this.__defineGetter__(“bar”, function(){
return _bar;
});

this.__defineSetter__(“bar”, function(val){
_bar = bar;
});
};
var foo = new Foo;
foo.bar = 123;
alert(foo.bar);

Определяем поддержку браузером

Из этого можно получить лёгкий способ определения, поддерживает ли браузер геттеры или не поддерживает:

return (typeof {}.__defineGetter__ == ‘function’);

Как быть с наследованием?

Получить саму функцию геттера или сеттера можно через методы .__lookupGetter__ и .__lookupSetter__.

function extend(target, source) {
for ( var prop in source ) {
var getter = source.__lookupGetter__(prop),
setter = source.__lookupSetter__(prop);

if ( getter || setter ) {
if ( getter ) target.__defineGetter__(prop, getter);
if ( setter ) target.__defineSetter__(prop, setter);
} else
a[i] = b[i];
}
return target;
}

Таким образом нашему target передадутся не значения родительского source, а функции-геттеры/сеттеры.

Что следует помнить

Некоторые замечания от John Resig:

* Для каждого свойства вы можете установить только один геттер и/или сеттер. Не может быть два геттера или сеттера
* Единственный способ удалить геттер или сеттер — это вызвать delete object[name];. Эта команда удаляет и геттер и сеттер, потому если вы хотите удалить что-то одно, а другое — оставить, надо сначала сохранить его, а после удаления — снова присвоить
* Когда вы используете __defineGetter__ или __defineSetter__ он просто тихонько перезаписывает предыдущий геттер или сеттер и даже удаляет само свойство с таким именем.
* Проверить, поддерживает ли ваш браузер геттеры и сеттеры можно с помощью простого сниппета:
javascript:foo={get test(){ return “foo”; }};alert(foo.test);

MooTools

Мутулз не поддерживает по-умолчанию такую возможность. И, хотя я уже предложил патч, мы можем с лёгкостью (слегка изменив исходники) заставить его понимать геттеры и сеттеры.
Итак, какая наша цель?

var Foo = new Class({
set test : function () { console.log(‘test is set’); },
get test : function () { console.log(‘test is got’); return ‘test’; },
});
foo.test = 1234; // test is set
alert(foo.test); // test is get

Более того, в классах унаследованных через Implements и Extends тоже должны работать геттеры и сеттеры родительского класса. Все наши действия будут происходить в файле [name: Class] внутри анонимной функции.
Во-первых, внутри функции, в самом верху, определим функцию, которая перезаписывает только геттеры и сеттеры. И хотя мы отказалась от устаревших браузеров — стоит застраховаться.

var implementGettersSetters = (typeof {}.__lookupGetter__ == ‘function’) ?
function (target, source) {
for (var key in source) {
var g = source.__lookupGetter__(key),
s = source.__lookupSetter__(key);
if ( g || s ) {
if (g) target.__defineGetter__(key, g);
if (s) target.__defineSetter__(key, s);
}
}
return target;
} : function (target, source) { return target; };

Конечно, если наш скрипт с такими геттерами попадёт в устаревший браузер, то он просто упадёт, но это страховка от того, чтобы кто-то случайно не взял этот файл и не прицепил его к себе на сайт, а потом недоумевал, что такое с ишаком.
Мы видим, что если __lookupGetter__ не поддерживается, то функция просто ничего не сделает.

Теперь заставляем работать getterы и setterы во время создания класса и наследования (Extends). Для этого:

// после
var Class = this.Class = new Type(‘Class’, function(params){

// сразу перед
newClass.$constructor = Class;
newClass.prototype.$constructor = newClass;
newClass.prototype.parent = parent;

// Необходимо вставить функцию, которая расширит прототип (внимание, прототип, а не объект!) нашего класса:
implementGettersSetters(newClass.prototype, params);

Отдельным движением надо реализовать наследование геттеров и сеттеров от примесей (Implements). Для этого надо найти встроенные Мутаторы и добавить всего одну строку:

Class.Mutators = {
// …
Implements: function(items){
Array.from(items).each(function(item){
var instance = new item;
for (var key in instance) implement.call(this, key, instance[key], true);
// Вот она:
implementGettersSetters(this.prototype, instance);
}, this);
}
};

Все, теперь сеттеры и геттеры реализуются и мы с лёгкостью можем их наследовать и использовать. Наслаждайтесь)

var Foo = new Class({
initialize : function (name) {
this.name = name;
},
set test : function () {
console.log(this.name + ‘ test is set’);
},
get test : function () {
console.log(this.name + ‘ test is got’);
return ‘test’;
},
});
var Bar = new Class({
Extends : Foo
});
var Qux = new Class({
Implements : [ Foo ]
});
var foo = new Foo(‘foo’);
foo.test = 1234; // foo test is set
alert(foo.test); // foo test is got

var bar = new Bar(‘bar’);
bar.test = 1234; // bar test is set
alert(bar.test); // bar test is got

var qux = new Qux(‘qux’);
qux.test = 1234; // qux test is set
alert(qux.test); // qux test is got

Читайте также:  Какими свойствами обладает научная информация

Интересные ссылки:

Object.defineProperty — средство для создания свойств с очень широкими настройкам, такие как writable, get, set, configurable, enumerable
Object.create — удобно быстро создавать нужные объекты.

Источник

Свойства

Последнее обновление: 03.10.2019

Кроме обычных методов в языке C# предусмотрены специальные методы доступа, которые называют свойства.
Они обеспечивают простой доступ к полям классов и структур, узнать их значение или выполнить их установку.

Стандартное описание свойства имеет следующий синтаксис:

[модификатор_доступа] возвращаемый_тип произвольное_название
{
// код свойства
}

Например:

class Person
{
private string name;

public string Name
{
get
{
return name;
}

set
{
name = value;
}
}
}

Здесь у нас есть закрытое поле name и есть общедоступное свойство Name. Хотя они имеют практически одинаковое название за исключением регистра,
но это не более чем стиль, названия у них могут быть произвольные и не обязательно должны совпадать.

Через это свойство мы можем управлять доступом к переменной name. Стандартное определение свойства содержит блоки get
и set. В блоке get мы возвращаем значение поля, а в блоке set устанавливаем. Параметр value представляет передаваемое значение.

Мы можем использовать данное свойство следующим образом:

Person p = new Person();

// Устанавливаем свойство – срабатывает блок Set
// значение “Tom” и есть передаваемое в свойство value
p.Name = “Tom”;

// Получаем значение свойства и присваиваем его переменной – срабатывает блок Get
string personName = p.Name;

Возможно, может возникнуть вопрос, зачем нужны свойства, если мы можем в данной ситуации обходиться обычными полями класса? Но свойства
позволяют вложить дополнительную логику, которая может быть необходима, например, при присвоении переменной класса какого-либо значения.
Например, нам надо установить проверку по возрасту:

class Person
{
private int age;

public int Age
{
set
{
if (value < 18)
{
Console.WriteLine(“Возраст должен быть больше 17”);
}
else
{
age = value;
}
}
get { return age; }
}
}

Если бы переменная age была бы публичной, то мы могли бы передать ей извне любое значение, в том числе отрицательное. Свойство же позволяет скрыть данные объекты и
опосредовать к ним доступ.

Блоки set и get не обязательно одновременно должны присутствовать в свойстве. Если свойство определяют только блок get, то такое свойство доступно только для
чтения – мы можем получить его значение, но не установить. И, наоборот, если свойство имеет только блок set, тогда это свойство доступно только для записи – можно
только установить значение, но нельзя получить:

class Person
{
private string name;
// свойство только для чтения
public string Name
{
get
{
return name;
}
}

private int age;
// свойство только для записи
public int Age
{
set
{
age = value;
}
}
}

Хотя в примерах выше свойства определялись в классе, но точно также мы можем определять и использовать свойства в структурах.

Модификаторы доступа

Мы можем применять модификаторы доступа не только ко всему свойству, но и к отдельным блокам – либо get, либо set:

class Person
{
private string name;

public string Name
{
get
{
return name;
}

private set
{
name = value;
}
}
public Person(string name)
{
Name = name;
}
}

Теперь закрытый блок set мы сможем использовать только в данном классе – в его методах, свойствах, конструкторе, но никак не в другом классе:

Person p = new Person(“Tom”);

// Ошибка – set объявлен с модификатором private
//p.Name = “John”;

Console.WriteLine(p.Name);

При использовании модификаторов в свойствах следует учитывать ряд ограничений:

  • Модификатор для блока set или get можно установить, если свойство имеет оба блока (и set, и get)

  • Только один блок set или get может иметь модификатор доступа, но не оба сразу

  • Модификатор доступа блока set или get должен быть более ограничивающим, чем модификатор доступа свойства. Например,
    если свойство имеет модификатор public, то блок set/get может иметь только модификаторы protected internal, internal, protected, private

Автоматические свойства

Свойства управляют доступом к полям класса. Однако что, если у нас с десяток и более полей, то определять каждое поле и писать для него однотипное свойство
было бы утомительно. Поэтому в фреймворк .NET были добавлены автоматические свойства. Они имеют сокращенное объявление:

class Person
{
public string Name { get; set; }
public int Age { get; set; }

public Person(string name, int age)
{
Name = name;
Age = age;
}
}

На самом деле тут также создаются поля для свойств, только их создает не программист в коде, а компилятор автоматически генерирует при компиляции.

В чем преимущество автосвойств, если по сути они просто обращаются к автоматически создаваемой переменной, почему бы напрямую не обратиться к переменной без автосвойств?
Дело в том, что в любой момент времени при необходимости мы можем развернуть автосвойство в обычное свойство, добавить в него какую-то определенную логику.

Стоит учитывать, что нельзя создать автоматическое свойство только для записи, как в случае со стандартными свойствами.

Автосвойствам можно присвоить значения по умолчанию (инициализация автосвойств):

class Person
{
public string Name { get; set; } = “Tom”;
public int Age { get; set; } = 23;
}

class Program
{
static void Main(string[] args)
{
Person person = new Person();
Console.WriteLine(person.Name); // Tom
Console.WriteLine(person.Age); // 23

Console.Read();
}
}

И если мы не укажем для объекта Person значения свойств Name и Age, то будут действовать значения по умолчанию.

Стоит отметить, что в структурах мы не можем использовать инициализацию автосвойств.

Автосвойства также могут иметь модификаторы доступа:

class Person
{
public string Name { private set; get;}
public Person(string n)
{
Name = n;
}
}

Мы можем убрать блок set и сделать автосвойство доступным только для чтения. В этом случае для хранения значения этого свойства для него неявно будет создаваться поле с модификатором readonly, поэтому следует учитывать, что подобные get-свойства
можно установить либо из конструктора класса, как в примере выше, либо при инициализации свойства:

class Person
{
public string Name { get;} = “Tom”
}

Сокращенная запись свойств

Как и методы, мы можем сокращать свойства. Например:

class Person
{
private string name;

// эквивалентно public string Name { get { return name; } }
public string Name => name;
}

Источник