Какие методы есть у свойства программирование
- 03/10/2017
- Чтение занимает 4 мин
В этой статье
Свойство — это член, предоставляющий гибкий механизм для чтения, записи или вычисления значения частного поля.A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field. Свойства можно использовать, как если бы они были членами общих данных, но фактически они представляют собой специальные методы, называемые методами доступа.Properties can be used as if they are public data members, but they are actually special methods called accessors. Это позволяет легко получать доступ к данным и помогает повысить безопасность и гибкость методов.This enables data to be accessed easily and still helps promote the safety and flexibility of methods.
Общие сведения о свойствахProperties overview
Свойства позволяют классу предоставлять общий способ получения и задания значений, скрывая при этом код реализации или проверки.Properties enable a class to expose a public way of getting and setting values, while hiding implementation or verification code.
Метод доступа get используется для возврата значения свойства, а метод доступа set — для присвоения нового значения.A get property accessor is used to return the property value, and a set property accessor is used to assign a new value. Эти методы доступа могут иметь различные уровни доступа.These accessors can have different access levels. Дополнительные сведения см. в разделе Доступность методов доступа.For more information, see Restricting Accessor Accessibility.
Ключевое слово value используется для определения значения, присваиваемого методом доступа set.The value keyword is used to define the value being assigned by the set accessor.
Свойства могут быть доступны для чтения и записи (они имеют оба метода доступа — get и set), только для чтения (они имеют метод доступа get, но не имеют метода доступа set) или только для записи (они имеют метод доступа set, но не имеют метода доступа get).Properties can be read-write (they have both a get and a set accessor), read-only (they have a get accessor but no set accessor), or write-only (they have a set accessor, but no get accessor). Свойства только для записи встречаются редко и чаще всего используются для ограничения доступа к конфиденциальным данным.Write-only properties are rare and are most commonly used to restrict access to sensitive data.
Простые свойства, не требующие пользовательского кода метода доступа, можно реализовать как определения текста выражений или как автоматически реализуемые свойства.Simple properties that require no custom accessor code can be implemented either as expression body definitions or as auto-implemented properties.
Свойства с резервными полямиProperties with backing fields
Одной из базовых схем реализации свойств является использование закрытого резервного поля для установки и извлечения значения свойства.One basic pattern for implementing a property involves using a private backing field for setting and retrieving the property value. Метод доступа get возвращает значение закрытого поля, а метод доступа set может выполнять определенные проверки данных до присвоения значению закрытого поля.The get accessor returns the value of the private field, and the set accessor may perform some data validation before assigning a value to the private field. Оба метода доступа также могут выполнять некоторые преобразования или вычисления данных до их сохранения или возвращения.Both accessors may also perform some conversion or computation on the data before it is stored or returned.
Это показано в следующем примере.The following example illustrates this pattern. В этом примере класс TimePeriod представляет интервал времени.In this example, the TimePeriod class represents an interval of time. На внутреннем уровне класс сохраняет интервал времени в секундах в закрытом поле с именем _seconds.Internally, the class stores the time interval in seconds in a private field named _seconds. Свойство чтения и записи с именем Hours позволяет клиенту указывать временной интервал в часах.A read-write property named Hours allows the customer to specify the time interval in hours. Методы доступа get и set выполняют необходимое преобразование между часами и секундами.Both the get and the set accessors perform the necessary conversion between hours and seconds. Кроме того, метод доступа set проверяет данные и создает ArgumentOutOfRangeException, если указано недопустимое количество часов.In addition, the set accessor validates the data and throws an ArgumentOutOfRangeException if the number of hours is invalid.
using System;
class TimePeriod
{
private double _seconds;
public double Hours
{
get { return _seconds / 3600; }
set {
if (value < 0 || value > 24)
throw new ArgumentOutOfRangeException(
$»{nameof(value)} must be between 0 and 24.»);
_seconds = value * 3600;
}
}
}
class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();
// The property assignment causes the ‘set’ accessor to be called.
t.Hours = 24;
// Retrieving the property causes the ‘get’ accessor to be called.
Console.WriteLine($»Time in hours: {t.Hours}»);
}
}
// The example displays the following output:
// Time in hours: 24
Определения текста выраженийExpression body definitions
Как правило, методы доступа к свойствам состоят из однострочных операторов, которые просто назначают или возвращают результат выражения.Property accessors often consist of single-line statements that just assign or return the result of an expression. Эти свойства можно реализовать как члены, воплощающие выражение.You can implement these properties as expression-bodied members. Определения текста выражений состоят из символа =>, за которым идет выражение, назначаемое свойству или извлекаемое из него.Expression body definitions consist of the => symbol followed by the expression to assign to or retrieve from the property.
Начиная с версии C# 6 свойства только для чтения могут реализовывать метод доступа get как член, воплощающий выражение.Starting with C# 6, read-only properties can implement the get accessor as an expression-bodied member. В этом случае не используется ни ключевое слово метода доступа get, ни ключевое слово return.In this case, neither the get accessor keyword nor the return keyword is used. В следующем примере показана реализация свойства только для чтения Name в виде члена, воплощающего выражение.The following example implements the read-only Name property as an expression-bodied member.
using System;
public class Person
{
private string _firstName;
private string _lastName;
public Person(string first, string last)
{
_firstName = first;
_lastName = last;
}
public string Name => $»{_firstName} {_lastName}»;
}
public class Example
{
public static void Main()
{
var person = new Person(«Magnus», «Hedlund»);
Console.WriteLine(person.Name);
}
}
// The example displays the following output:
// Magnus Hedlund
Начиная с C# 7.0 методы доступа get и set можно реализовывать в виде членов, воплощающих выражения.Starting with C# 7.0, both the get and the set accessor can be implemented as expression-bodied members. В этом случае необходимо указывать ключевые слова get и set.In this case, the get and set keywords must be present. В следующем примере показано использование определений текста выражений для обоих методов доступа.The following example illustrates the use of expression body definitions for both accessors. Обратите внимание, что ключевое слово return не используется с методом доступа get.Note that the return keyword is not used with the get accessor.
using System;
public class SaleItem
{
string _name;
decimal _cost;
public SaleItem(string name, decimal cost)
{
_name = name;
_cost = cost;
}
public string Name
{
get => _name;
set => _name = value;
}
public decimal Price
{
get => _cost;
set => _cost = value;
}
}
class Program
{
static void Main(string[] args)
{
var item = new SaleItem(«Shoes», 19.95m);
Console.WriteLine($»{item.Name}: sells for {item.Price:C2}»);
}
}
// The example displays output like the following:
// Shoes: sells for $19.95
Автоматически реализуемые свойстваAuto-implemented properties
В некоторых случаях свойство get и методы доступа set просто присваивают значение резервному полю или извлекают значение из него без включения дополнительной логики.In some cases, property get and set accessors just assign a value to or retrieve a value from a backing field without including any additional logic. С помощью автоматически реализуемых свойств можно упростить код, в то время как компилятор C# будет прозрачно предоставлять вам резервное поле.By using auto-implemented properties, you can simplify your code while having the C# compiler transparently provide the backing field for you.
Если у свойства есть методы доступа get и set, они оба должны быть автоматически реализованы.If a property has both a get and a set accessor, both must be auto-implemented. Автоматически реализуемое свойство определяется с помощью ключевых слов get и set без указания какой-либо реализации.You define an auto-implemented property by using the get and set keywords without providing any implementation. Следующий пример аналогичен предыдущему, за исключением того, что Name и Price являются автоматически реализуемыми свойствами.The following example repeats the previous one, except that Name and Price are auto-implemented properties. Обратите внимание на то, что в этом примере также удаляется параметризованный конструктор, что позволяет инициализировать объекты SaleItem, вызывая конструктор без параметров и инициализатор объекта.Note that the example also removes the parameterized constructor, so that SaleItem objects are now initialized with a call to the parameterless constructor and an object initializer.
using System;
public class SaleItem
{
public string Name
{ get; set; }
public decimal Price
{ get; set; }
}
class Program
{
static void Main(string[] args)
{
var item = new SaleItem{ Name = «Shoes», Price = 19.95m };
Console.WriteLine($»{item.Name}: sells for {item.Price:C2}»);
}
}
// The example displays output like the following:
// Shoes: sells for $19.95
Использование свойствUsing Properties
Свойства интерфейсаInterface Properties
Сравнение свойств и индексаторовComparison Between Properties and Indexers
Ограничение доступности методов доступаRestricting Accessor Accessibility
Автоматически реализуемые свойстваAuto-Implemented Properties
Спецификация языка C#C# Language Specification
Дополнительные сведения см. в разделе Свойства в Спецификации языка C#.For more information, see Properties in the C# Language Specification. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.The language specification is the definitive source for C# syntax and usage.
См. также разделSee also
- Руководство по программированию на C#C# Programming Guide
- Использование свойствUsing Properties
- ИндексаторыIndexers
- Ключевое слово getget keyword
- Ключевое слово setset keyword
Свойства
Последнее обновление: 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;
}
Статья не мальчика, но мужа.
Настало время серьёзных тем: сегодня расскажем про объектно-ориентированное программирование, или ООП. Это тема для продвинутого уровня разработки, и мы хотим, чтобы вы его постигли.
Из этого термина можно сделать вывод, что ООП — это такой подход к программированию, где на первом месте стоят объекты. На самом деле там всё немного сложнее, но мы до этого ещё доберёмся. Для начала поговорим про ООП вообще и разберём, с чего оно начинается.
Обычное программирование (процедурное)
Чаще всего под обычным понимают процедурное программирование, в основе которого — процедуры и функции. Функция — это мини-программа, которая получает на вход какие-то данные, что-то делает внутри себя и может отдавать какие-то данные в результате вычислений. Представьте, что это такой конвейер, который упакован в коробочку.
Например, в интернет-магазине может быть функция «Проверить email». Она получает на вход какой-то текст, сопоставляет со своими правилами и выдаёт ответ: это правильный электронный адрес или нет. Если правильный, то true, если нет — то false.
Функции полезны, когда нужно упаковать много команд в одну. Например, проверка электронного адреса может состоять из одной проверки на регулярные выражения, а может содержать множество команд: запросы в словари, проверку по базам спамеров и даже сопоставление с уже известными электронными адресами. В функцию можно упаковать любой комбайн из действий и потом просто вызывать их все одним движением.
Что не так с процедурным программированием
Процедурное программирование идеально работает в простых программах, где все задачи можно решить, грубо говоря, десятком функций. Функции аккуратно вложены друг в друга, взаимодействуют друг с другом, можно передать данные из одной функции в другую.
Например, вы пишете функцию «Зарегистрировать пользователя интернет-магазина». Внутри неё вам нужно проверить его электронный адрес. Вы вызываете функцию «Проверить email» внутри функции «Зарегистрировать пользователя», и в зависимости от ответа функции вы либо регистрируете пользователя, либо выводите ошибку. И у вас эта функция встречается ещё в десяти местах. Функции как бы переплетены.
Тут приходит продакт-менеджер и говорит: «Хочу, чтобы пользователь точно знал, в чём ошибка при вводе электронного адреса». Теперь вам нужно научить функцию выдавать не просто true — false, а ещё и код ошибки: например, если в адресе опечатка, то код 01, если адрес спамерский — код 02 и так далее. Это несложно реализовать.
Вы залезаете внутрь этой функции и меняете её поведение: теперь она вместо true — false выдаёт код ошибки, а если ошибки нет — пишет «ОК».
И тут ваш код ломается: все десять мест, которые ожидали от проверяльщика true или false, теперь получают «ОК» и из-за этого ломаются.
Теперь вам нужно:
- либо переписывать все функции, чтобы научить их понимать новые ответы проверяльщика адресов;
- либо переделать сам проверяльщик адресов, чтобы он остался совместимым со старыми местами, но в нужном вам месте как-то ещё выдавал коды ошибок;
- либо написать новый проверяльщик, который выдаёт коды ошибок, а в старых местах использовать старый проверяльщик.
Задача, конечно, решаемая за час-другой.
Но теперь представьте, что у вас этих функций — сотни. И изменений в них нужно делать десятки в день. И каждое изменение, как правило, заставляет функции вести себя более сложным образом и выдавать более сложный результат. И каждое изменение в одном месте ломает три других места. В итоге у вас будут нарождаться десятки клонированных функций, в которых вы сначала будете разбираться, а потом уже нет.
Это называется спагетти-код, и для борьбы с ним как раз придумали объектно-ориентированное программирование.
Объектно-ориентированное программирование
Основная задача ООП — сделать сложный код проще. Для этого программу разбивают на независимые блоки, которые мы называем объектами.
Объект — это не какая-то космическая сущность. Это всего лишь набор данных и функций — таких же, как в традиционном функциональном программировании. Можно представить, что просто взяли кусок программы и положили его в коробку и закрыли крышку. Вот эта коробка с крышками — это объект.
Программисты договорились, что данные внутри объекта будут называться свойствами, а функции — методами. Но это просто слова, по сути это те же переменные и функции.
Объект можно представить как независимый электроприбор у вас на кухне. Чайник кипятит воду, плита греет, блендер взбивает, мясорубка делает фарш. Внутри каждого устройства куча всего: моторы, контроллеры, кнопки, пружины, предохранители — но вы о них не думаете. Вы нажимаете кнопки на панели каждого прибора, и он делает то, что от него ожидается. И благодаря совместной работе этих приборов у вас получается ужин.
Объекты характеризуются четырьмя словами: инкапсуляция, абстракция, наследование и полиморфизм.
Инкапсуляция — объект независим: каждый объект устроен так, что нужные для него данные живут внутри этого объекта, а не где-то снаружи в программе. Например, если у меня есть объект «Пользователь», то у меня в нём будут все данные о пользователе: и имя, и адрес, и всё остальное. И в нём же будут методы «Проверить адрес» или «Подписать на рассылку».
Абстракция — у объекта есть «интерфейс»: у объекта есть методы и свойства, к которым мы можем обратиться извне этого объекта. Так же, как мы можем нажать кнопку на блендере. У блендера есть много всего внутри, что заставляет его работать, но на главной панели есть только кнопка. Вот эта кнопка и есть абстрактный интерфейс.
В программе мы можем сказать: «Удалить пользователя». На языке ООП это будет «пользователь.удалить()» — то есть мы обращаемся к объекту «пользователь» и вызываем метод «удалить». Кайф в том, что нам не так важно, как именно будет происходить удаление: ООП позволяет нам не думать об этом в момент обращения.
Например, над магазином работают два программиста: один пишет модуль заказа, а второй — модуль доставки. У первого в объекте «заказ» есть метод «отменить». И вот второму нужно из-за доставки отменить заказ. И он спокойно пишет: «заказ.отменить()». Ему неважно, как другой программист будет реализовывать отмену: какие он отправит письма, что запишет в базу данных, какие выведет предупреждения.
Наследование — способность к копированию. ООП позволяет создавать много объектов по образу и подобию другого объекта. Это позволяет не копипастить код по двести раз, а один раз нормально написать и потом много раз использовать.
Например, у вас может быть некий идеальный объект «Пользователь»: в нём вы прописываете всё, что может происходить с пользователем. У вас могут быть свойства: имя, возраст, адрес, номер карты. И могут быть методы «Дать скидку», «Проверить заказ», «Найти заказы», «Позвонить».
На основе этого идеального пользователя вы можете создать реального «Покупателя Ивана». У него при создании будут все свойства и методы, которые вы задали у идеального покупателя, плюс могут быть какие-то свои, если захотите.
Идеальные объекты программисты называют классами.
Полиморфизм — единый язык общения. В ООП важно, чтобы все объекты общались друг с другом на понятном им языке. И если у разных объектов есть метод «Удалить», то он должен делать именно это и писаться везде одинаково. Нельзя, чтобы у одного объекта это было «Удалить», а у другого «Стереть».
При этом внутри объекта методы могут быть реализованы по-разному. Например, удалить товар — это выдать предупреждение, а потом пометить товар в базе данных как удалённый. А удалить пользователя — это отменить его покупки, отписать от рассылки и заархивировать историю его покупок. События разные, но для программиста это неважно. У него просто есть метод «Удалить()», и он ему доверяет.
Создать карусель Добавьте описание
Такой подход позволяет программировать каждый модуль независимо от остальных. Главное — заранее продумать, как модули будут общаться друг с другом и по каким правилам. При таком подходе вы можете улучшить работу одного модуля, не затрагивая остальные — для всей программы неважно, что внутри каждого блока, если правила работы с ним остались прежними.
Плюсы и минусы ООП
У объектно-ориентированного программирования много плюсов, и именно поэтому этот подход использует большинство современных программистов.
- Визуально код становится проще, и его легче читать. Когда всё разбито на объекты и у них есть понятный набор правил, можно сразу понять, за что отвечает каждый объект и из чего он состоит.
- Меньше одинакового кода. Если в обычном программировании одна функция считает повторяющиеся символы в одномерном массиве, а другая — в двумерном, то у них большая часть кода будет одинаковой. В ООП это решается наследованием.
- Сложные программы пишутся проще. Каждую большую программу можно разложить на несколько блоков, сделать им минимальное наполнение, а потом раз за разом подробно наполнить каждый блок.
- Увеличивается скорость написания. На старте можно быстро создать нужные компоненты внутри программы, чтобы получить минимально работающий прототип.
А теперь про минусы:
- Сложно понять и начать работать. Подход ООП намного сложнее обычного функционального программирования — нужно знать много теории, прежде чем будет написана хоть одна строчка кода.
- Требует больше памяти. Объекты в ООП состоят из данных, интерфейсов, методов и много другого, а это занимает намного больше памяти, чем простая переменная.
- Иногда производительность кода будет ниже. Из-за особенностей подхода часть вещей может быть реализована сложнее, чем могла бы быть. Поэтому бывает такое, что ООП-программа работает медленнее, чем функциональная (хотя с современными мощностями процессоров это мало кого волнует).
Что дальше
Впереди нас ждёт разговор о классах, объектах и всём остальном важном в ООП. Крепитесь, будет интересно!
Подписывайтесь на наш канал, чтобы стать настоящим профи!