В статье поговорим о свойствах полей класса (c# get set
). Разберемся с определением и назначением этой структуры, а также рассмотрим примеры внедрения этой концепции в C#-приложения.
Что такое свойства
Преимущества применения концепции свойств
Пример свойств типа read-write
Модификаторы свойств
Пример свойства в связке с массивом
Пример свойств типа read-only
Пример свойств типа write-only
Автоматические свойства
Доступ к структурной переменной
Обзор рассматриваемой концепции
Свойство в C# является членом класса, который используется для установки и получения данных из поля данных класса. Самый важный момент, о котором нужно помнить — свойство в C# никогда не используется для хранения данных, оно действует как интерфейс для их передачи. Это своего рода расширение переменных класса, предоставляющее возможность читать, записывать или изменять значения этих переменных, не влияя на внешний способ доступа к ним в приложении.
Оно содержит один или два блока кода, называемых аксессорами, внутри которых находятся методы доступа get
и set
. Используя эти методы, мы можем изменить внутреннюю реализацию переменных класса в зависимости от наших требований.
Записываются свойства (property
) так:
<access_modifier> <return_type> <property_name>{ get{ // возвращает значение свойства } set{ // устанавливает новое } }
При написании свойств важно определиться с модификатором и типом возвращаемого значения, чтобы внести необходимые изменения в переменные класса (исходя из того, что нам требуется).
В такой конструкции код в методе get
будет выполняться всякий раз, когда свойство читается (возвращает результат), а код в методе set
будет отрабатывать, когда свойство переопределяется другим значением.
Такая структура данных в С#, подразделяются на три типа:
Read-Write
(для чтения-записи). Так называется свойство, содержащее два блока аксессора.Read-Only
(только для чтения). Содержит один блок-аксессор с методом get
внутри.Write-Only
(только для записи). Содержит один блок-аксессор с методом set
внутри.Также следует помнить, что свойства не принимают никаких параметров, а тип данных свойств должен быть таким же, как у полей, для которых мы его создаем.
Вот простейший пример определения приватной переменной со свойством:
class Item { private string color; public string Color { get { return color; } set { color = value; } } }
Здесь мы написали свойство «Color» с методами get
(для возврата значения) и set
(для его переопределения).
Сразу отметим, что в методе get
мы либо возвращаем, либо вычисляем, а затем возвращаем значение поля. Но тут важно понимать, что акссесор не используется для изменения состояния объекта! С помощью методов get
и set
можно лишь расширить поведение переменных:
class User{ private string fullname = "Sheldon Cooper"; public string FullName{ get{ return fullname.ToUpper(); } set{ if (value == "Sheldon Cooper") fullname = value; } } }
В примере рассмотрено написание частной переменной fullname
и ее свойства Fullname
. Их названия одинаковы, если не считать регистра, но это просто стиль написания. В своих примерах вы можете называть их по-разному, их названия могут быть какие угодно. Параметр value
служит для определения передаваемого значения.
Таким образом применение концепции свойств дает полный контроль над доступом к полям класса.
Вот как это выглядит:
User u = new User(); u.Fullname = "Howard Wolowitz"; // срабатывает аксессор set Console.WriteLine(u.Fullname); // срабатывает аксессор get
Здесь мы наделяем наше свойство новым значением с вызовом set
, а get
сработает когда мы попытаемся прочитать это значение.
Вы можете задаться вопросом: для чего так необходимы эти свойства? Ведь в подобных ситуациях можно обойтись обыкновенными переменными класса. Вся суть концепции свойств заключается в возможности вкладывания в переменные дополнительной логики.
Например, при присваивании ей другого значения:
class Human{ private int years; public int Years{ set{ if(value < 18){ Console.WriteLine("Доступ только совершеннолетним!"); } else{ years = value; } } get {return years;} } }
Имей переменная years из нашего примера public-доступ — у разработчика появилась бы возможность присвоить ей извне другое значение, что не всегда хорошо (подобные вещи чаще всего происходят в результате ошибки или случайности). В таких ситуациях нам и необходимы свойства, ограничивающие к ней доступ.
А еще, применяя свойства, мы:
set
, применив «readonly-подход».Рассмотрим пример реализации класса Exmpl
с одним целочисленным полем, используемым в качестве хранилища для свойства Numb
, обеспечивающего реализацию стандартных методов-аксессоров.
using System; class Exmpl{ int _numb; public int Numb{ get{ return this._numb; } set{ this._numb = value; } } } class Prog{ static void Main(){ Exmpl exmpl = new Exmpl(); exmpl.Numb = 5; // set { } Console.WriteLine(example.Numb); // get { } } }
Выведет:5
Для использования нашего свойства необходимо объявить его открытым (public). Иначе нам не удастся получить к нему доступ и результатом будет ошибка компилятора. Также можно использовать модификаторы отдельно во внутренних блоках свойства.
Однако тут мы должны помнить о ряде существующих ограничений:
protected
, private
, internal
, protected internal
. Рассмотрим пример по реализации свойства double-массива. Объявим:
X
— числовой массив Summ
namespace Highload{ class ArrDouble{ double[] X; // массив public ArrDouble(int size){ // выделение ячейки памяти нашему массиву X = new double[size]; for (int i = 0; i < size; i++) X[i] = 0.0; } // заполнение массива нулевыми значениями public ArrDouble(double[] _X){ X = new double[_X.Length]; // копируем массив for (int i = 0; i < _X.Length; i++) X[i] = _X[i]; } public double this[int index]{ get{ if ((index >= 0) && (index < X.Length)) return X[index]; else return 0.0; } } // индексация */Объявление свойства Summary c методом get, находящим сумму всех членов массива и set, раздающим этим членам значения/* public double Summary{ get{ // суммируем элементы double summ = 0; for (int i = 0; i < X.Length; i++) summ += X[i]; return summ; } set{ // Равномерное распределение значений между членами массива double val = value/X.Length; for (int i = 0; i < X.Length; i++) X[i] = val; } } } class Prog { static void Main(string[] args) { double[] X = { 1.0, 2.5, 2.0, 4.5, 0.0 }; ArrDouble ad = new ArrDouble(X); // создание экземпляра ArrDouble double summary; summ = ad.Summ; Console.WriteLine("summ = {0}", summ); // заполняем ad единицами ad.Summ = 1.0 * X.Length; // проверяем double test; test = ad[2]; // test = 1.0 Console.WriteLine("test= {0}", test); } } }
Выведет:
summ = 10
test = 1
В Summary get
вычисляет сумму всех членов массива, а set
— раздает им значение.
Мы рассмотрели ранее, что свойство с одним методом get
принадлежит типу read-only
. Приведем пример создания такого рода свойств:
using System; namespace Highload{ class Human{ private string fullname; private string loc; public Human(string a, string b){ fullname = a; loc = b; } public string FullName{ get{ return fullname; } } public string Loc{ get{ return loc; } } } class Prog{ static void Main(string[] args){ Human u = new Human("Sheldon Cooper", "Pasadena"); // ошибка, которую выдаст компилятор // u.FullName = "Howard Wolowitz"; // get accessor will invoke Console.WriteLine("FullName: " + u.FullName); // get accessor will invoke Console.WriteLine("Loc: " + u.Loc); } } }
Здесь мы применили только один метод доступа — get
, сделав тем самым наше свойство доступным только для чтения. Раскомментировав закомментированный код, мы получим ошибку компиляции из-за отсутствия возможности установить новое значение при помощи set
.
Запустив нашу программу, мы получим следующий результат:
Name: Sheldon Cooper
Loc: Pasadena
Пример:
using System; namespace Highload{ class Pers{ private string fullname; public string FullName{ set{ fullname = value; } } private string loc; public string Loc{ set{ loc = value; } } public void GetPersonDetails(){ Console.WriteLine("FullName: " + fullname); Console.WriteLine("Loc: " + loc); } } class Prog{ static void Main(string[] args){ Pers u = new Pers(); u.FullName = "Sheldon Cooper"; u.Loc = "Pasadena"; // компилятор выдаст ошибку //Console.WriteLine(u.FullName); u.GetPersonDetails(); } } }
Здесь, наоборот, мы применили только один метод — set
, сделав свойство Write-only
. А раскомментировав ранее закомментированный код, мы получим ошибку компиляции из-за отсутствия возможности возврата значения.
Результат программы будет такой же, как и в предыдущем примере.
Свойства управляют доступностью переменных класса, и если такая переменная одна — тогда все понятно. Но что если у нас их несколько десятков? Ведь в таком случае писать каждой однотипное свойство — долго и затратно. Для решения такого рода вопросов в C# , а точнее в его фреймворк .NET, был добавлен функционал реализации автоматических свойств. Давайте разберемся, что это такое.
Автоматическим называется свойство, содержащее стандартные методы доступа (get, set
) без какой-либо логической реализации, например:
using System; namespace Highload{ class Pers{ public string FullName { get; set; } public string Loc { get; set; } } class Prog{ static void Main(string[] args){ Pers u = new Pers(); u.FullName = "Sheldon Cooper"; u.Loc = "Pasadena"; Console.WriteLine("FullName: " + u.FullName); Console.WriteLine("Loc: " + u.Loc); } } }
Такой подход уменьшает объем написанного кода. При применении автоматических свойств компилятор С# неявно создает частное, анонимное поле за кулисами для получения данных.
Рассмотрим принцип работы связки свойств и структурной переменной типа fraction
, реализующей дробь:
using System; namespace Highload{ struct Frctn{ public int nume; // numerator public int deno; // denominator } // класс комплексного числа class ComplexNumber{ private Frctn real; private Frctn im; // несколько конструкторов public ComplexNumber(int _real, int _im){ real.nume = _real; real.deno = 1; im.nume = _im; im.deno = 1; } public ComplexNumber(Frctn _real, Frctn _im){ real = _real; im = _im; } // свойство действительной части public Frctn Real{ get{ return real; } set{ real = value; } } // свойство мнимой части public Frctn Im{ get{ return im; } set{ im = value; } } public double RealD{ get{ return (double)real.nume/(double)real.deno; } } public double ImD{ get{ return (double)im.nume / (double)im.deno; } } // свойство, возвращающее модуль этого числа public double Ab{ get{ double x,y,z; x = real.nume / (double)real.deno; y = im.nume / (double)im.deno; z = Math.Sqrt(y * y + x * x); return z; } } } class Prog{ static void Main(string[] args){ Frctn r = new Frctn(); Frctn i = new Frctn(); r.nume = 3; r.deno = 1; i.nume = 4; i.deno = 1; ComplexNumber c = new ComplexNumber(x, y); // экземпляр класса ComplexNumber // свойства класса ComplexNumber double ab = c.Ab; // берем модуль, ab = 5 Console.WriteLine("Ab = {0}", ab); double rd, id; rd = c.RealD; // rd = 3/1 = 3.0 id = c.ImD; // id = 4/1 = 4.0 Console.WriteLine("rd = {0}", rd); // свойство Real Frctn R = new Fraction(); R = c.Real; // R = { 3, 1 } Console.Write("R.nume = {0}, ", R.nume); Console.WriteLine("R.deno = {0}", R.deno); } } }
Программа выведет:
Ab = 5
rd = 3
R.nume = 3, R.deno = 1
Рассматривая свойства, надо отметить, что они не добавляют место в памяти для переменных класса, а только контролируют доступ к ним. А также не являются членами данных класса, а переменные определяются независимо от них.
Давайте кратко перечислим наиболее важные моменты, которые необходимо четко понимать при работе со свойствами:
get
нужен для возврата значения свойства, а set
— для переопределения этого значения.value
— ключевое слово в set
-аксессоре, используемое для назначения ему значенияRead-Write
(чтение запись) с двумя аксессорами внутриRead only
(только для чтения) — отсутствует метод setWrite only
(только для записи) — отсутствует метод get Для лучшего восприятия и освоения рассматриваемой концепции предлагаю посмотреть видеоролик по теме:
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…