Что такое инкапсуляция в си шарп
Перейти к содержимому

Что такое инкапсуляция в си шарп

  • автор:

Основы объектно-ориентированного программирования

Все основанные на объектах языки (C#, Java, С++, Smalltalk, Visual Basic и т.п.) должны отвечать трем основным принципам объектно-ориентированного программирования (ООП), которые перечислены ниже:

Инкапсуляция

Как данный язык скрывает детали внутренней реализации объектов и предохраняет целостность данных?

Наследование

Как данный язык стимулирует многократное использование кода?

Полиморфизм

Как данный язык позволяет трактовать связанные объекты сходным образом?

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

Роль инкапсуляции

— это механизм программирования, объединяющий вместе код и данные, которыми он манипулирует, исключая как вмешательство извне, так и неправильное использование данных. В объектно-ориентированном языке данные и код могут быть объединены в совершенно автономный черный ящик. Внутри такого ящика находятся все необходимые данные и код. Когда код и данные связываются вместе подобным образом, создается объект. Иными словами, объект — это элемент, поддерживающий инкапсуляцию.

Т.е. инкапсуляция представляет собой способности языка скрывать излишние детали реализации от пользователя объекта. Например, предположим, что используется класс по имени DatabaseReader, который имеет два главных метода: Open() и Close().

Фиктивный класс DatabaseReader инкапсулирует внутренние детали нахождения, загрузки, манипуляций и закрытия файла данных. Программистам нравится инкапсуляция, поскольку этот принцип ООП упрощает кодирование. Нет необходимости беспокоиться о многочисленных строках кода, которые работают «за кулисами», чтобы реализовать функционирование класса DatabaseReader. Все, что потребуется — это создать экземпляр и отправлять ему соответствующие сообщения (например, «открыть файл по имени AutoLot.mdf, расположенный на диске С:»).

С идеей инкапсуляции программной логики тесно связана идея защиты данных. В идеале данные состояния объекта должны быть специфицированы с использованием ключевого слова private (или, возможно, protected). Таким образом, внешний мир должен вежливо попросить, если захочет изменить или получить лежащее в основе значение. Это хороший принцип, поскольку общедоступные элементы данных можно легко повредить (даже нечаянно, а не преднамеренно).

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

Код и данные, составляющие вместе класс, называют членами. Данные, определяемые классом, называют полями, или переменными экземпляра. А код, оперирующий данными, содержится в функциях-членах, самым типичным представителем которых является метод. В C# метод служит в качестве аналога подпрограммы. (К числу других функций-членов относятся свойства, события и конструкторы.) Таким образом, методы класса содержат код, воздействующий на поля, определяемые этим классом.

Роль наследования

Следующий принцип ООП — наследование — касается способности языка позволять строить новые определения классов на основе определений существующих классов. По сути, наследование позволяет расширять поведение базового (или родительского) класса, наследуя основную функциональность в производном подклассе (также именуемом дочерним классом):

Принцип наследования

Т.е. наследование представляет собой процесс, в ходе которого один объект приобретает свойства другого объекта. Это очень важный процесс, поскольку он обеспечивает принцип иерархической классификации. Если вдуматься, то большая часть знаний поддается систематизации благодаря иерархической классификации по нисходящей.

Если не пользоваться иерархиями, то для каждого объекта пришлось бы явно определять все его свойства. А если воспользоваться наследованием, то достаточно определить лишь те свойства, которые делают объект особенным в его классе. Он может также наследовать общие свойства своего родителя. Следовательно, благодаря механизму наследования один объект становится отдельным экземпляром более общего класса.

Роль полиморфизма

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

По сути, — это член базового класса, определяющий реализацию по умолчанию, которая может быть изменена (или, говоря более формально, переопределена) в производном классе. В отличие от него, — это член базового класса, который не предусматривает реализации по умолчанию, а предлагает только сигнатуру. Когда класс наследуется от базового класса, определяющего абстрактный метод, этот метод обязательно должен быть переопределен в производном классе. В любом случае, когда производные классы переопределяют члены, определенные в базовом классе, они по существу переопределяют свою реакцию на один и тот же запрос.

Рассмотрим для примера стек, т.е. область памяти, функционирующую по принципу «последним пришел — первым обслужен». Допустим, что в программе требуются три разных типа стеков: один — для целых значений, другой — для значений с плавающей точкой, третий — для символьных значений. В данном примере алгоритм, реализующий все эти стеки, остается неизменным, несмотря на то, что в них сохраняются разнотипные данные. В языке, не являющемся объектно-ориентированным, для этой цели пришлось бы создать три разных набора стековых подпрограмм с разными именами. Но благодаря полиморфизму для реализации всех трех типов стеков в C# достаточно создать лишь один общий набор подпрограмм. Зная, как пользоваться одним стеком, вы сумеете воспользоваться и остальными.

В более общем смысле понятие полиморфизма нередко выражается следующим образом: «один интерфейс — множество методов«. Это означает, что для группы взаимосвязанных действий можно разработать общий интерфейс. Полиморфизм помогает упростить программу, позволяя использовать один и тот же интерфейс для описания общего класса действий. Выбрать конкретное действие (т.е. метод) в каждом отдельном случае — это задача компилятора. Программисту не нужно делать это самому. Ему достаточно запомнить и правильно использовать общий интерфейс.

C # — Инкапсуляция

Инкапсуляция определяется как процесс включения одного или нескольких элементов в физический или логический пакет. Инкапсуляция в методологии объектно-ориентированного программирования препятствует доступу к деталям реализации.

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

Инкапсуляция реализуется с использованием спецификаторов доступа . Спецификатор доступа определяет область видимости члена класса. C # поддерживает следующие спецификации доступа —

  • общественного
  • Частный
  • защищенный
  • внутренний
  • Защищенный внутренний

Спецификатор общего доступа

Открытый спецификатор доступа позволяет классу подвергать свои переменные-члены и функции-члены другим функциям и объектам. Доступ к любому публичному члену можно получить извне класса.

Следующий пример иллюстрирует это:

using System; namespace RectangleApplication < class Rectangle < //member variables public double length; public double width; public double GetArea() < return length * width; >public void Display() < Console.WriteLine("Length: ", length); Console.WriteLine("Width: ", width); Console.WriteLine("Area: ", GetArea()); > >//end class Rectangle class ExecuteRectangle < static void Main(string[] args) < Rectangle r = new Rectangle(); r.length = 4.5; r.width = 3.5; r.Display(); Console.ReadLine(); >> >

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Length: 4.5 Width: 3.5 Area: 15.75

В предыдущем примере длина и ширина переменных-членов объявляются общедоступными , поэтому к ним можно получить доступ из функции Main (), используя экземпляр класса Rectangle с именем r .

Функция участника Display () и GetArea () также может напрямую обращаться к этим переменным без использования какого-либо экземпляра класса.

Функции-члены Display () также объявляются общедоступными , поэтому к нему также можно получить доступ из Main (), используя экземпляр класса Rectangle с именем r .

Спецификатор частного доступа

Спецификатор частного доступа позволяет классу скрыть свои переменные-члены и функции-члены от других функций и объектов. Только функции одного класса могут обращаться к своим частным членам. Даже экземпляр класса не может получить доступ к своим частным членам.

Следующий пример иллюстрирует это:

using System; namespace RectangleApplication < class Rectangle < //member variables private double length; private double width; public void Acceptdetails() < Console.WriteLine("Enter Length: "); length = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("Enter Width: "); width = Convert.ToDouble(Console.ReadLine()); >public double GetArea() < return length * width; >public void Display() < Console.WriteLine("Length: ", length); Console.WriteLine("Width: ", width); Console.WriteLine("Area: ", GetArea()); > >//end class Rectangle class ExecuteRectangle < static void Main(string[] args) < Rectangle r = new Rectangle(); r.Acceptdetails(); r.Display(); Console.ReadLine(); >> >

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Enter Length: 4.4 Enter Width: 3.3 Length: 4.4 Width: 3.3 Area: 14.52

В предыдущем примере длина и ширина переменных-членов объявляются частными , поэтому к ним нельзя получить доступ из функции Main (). Функции-члены AcceptDetails () и Display () могут обращаться к этим переменным. Поскольку функции-члены AcceptDetails () и Display ()объявлены общедоступными , к ним можно получить доступ из Main (),используя экземпляр класса Rectangle с именем r .

Защищенный спецификатор доступа

Защищенный спецификатор доступа позволяет дочернему классу обращаться к переменным-членам и функциям-членам его базового класса. Таким образом, это помогает в реализации наследования. Мы обсудим это более подробно в главе наследования.

Внутренний спецификатор доступа

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

Следующая программа иллюстрирует это:

using System; namespace RectangleApplication < class Rectangle < //member variables internal double length; internal double width; double GetArea() < return length * width; >public void Display() < Console.WriteLine("Length: ", length); Console.WriteLine("Width: ", width); Console.WriteLine("Area: ", GetArea()); > >//end class Rectangle class ExecuteRectangle < static void Main(string[] args) < Rectangle r = new Rectangle(); r.length = 4.5; r.width = 3.5; r.Display(); Console.ReadLine(); >> >

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Length: 4.5 Width: 3.5 Area: 15.75

В предыдущем примере обратите внимание, что функция-член GetArea ()не объявлена ​​с помощью любого спецификатора доступа. Тогда какой будет спецификатор доступа по умолчанию для члена класса, если мы не будем упоминать о нем?

Защищенный спецификатор внутреннего доступа

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

Инкапсуляция в C#: 2 поясняющих примера

notebook code editor with encapsulation

Инкапсуляция — одно из основных понятий в объектно-ориентированном программировании (ООП), и в C# это понятие играет ключевую роль. Инкапсуляция можно сравнить с работой почтового ящика: вы можете отправлять письма (данные), зная только адрес почтового ящика (интерфейс объекта), но не знаете, что происходит внутри почтового отделения (внутренняя реализация объекта).

Что такое Инкапсуляция?

Инкапсуляция в программировании — это процесс упаковки данных (переменных) и кода (функций), работающего с этими данными, в один объект. Это помогает:

  • Скрыть внутреннюю реализацию класса от внешнего использования.
  • Сделать интерфейс взаимодействия с объектом простым и понятным.

Примеры инкапсуляции в C

Пример 1: Простой класс с инкапсуляцией

public class BankAccount < private double balance; // Приватное поле, не доступно извне public BankAccount(double initialBalance) < balance = initialBalance; >// Метод для добавления средств на счет public void Deposit(double amount) < if (amount > 0) < balance += amount; >> // Метод для получения текущего баланса public double GetBalance( ) < return balance; > > 

В этом примере переменная balance инкапсулирована в классе BankAccount . Она приватная, что означает, что доступ к ней возможен только через публичные методы Deposit и GetBalance .

Пример 2: Расширенная инкапсуляция с использованием свойств

public class Person < private string name; public string Name < get < return name; > set < if (!string.IsNullOrEmpty(value)) name = value; > > private int age; public int Age < get < return age; > private set < if (value > 0) age = value; > > public Person(string name, int age) < Name = name; Age = age; >public void CelebrateBirthday( ) < Age++; >> 

В этом примере мы используем свойства C# ( Name и Age ), чтобы обеспечить более тонкий контроль над тем, как внешний код может взаимодействовать с полями класса. Age устанавливается как приватное свойство, что означает, что его нельзя изменить извне класса напрямую, а Name требует непустой строки для установки.

Читайте так же Методы в C# — простое объяснение с примерами

Заключение

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

Что такое инкапсуляция в си шарп

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

Определение свойств

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

[модификаторы] тип_свойства название_свойства < get < действия, выполняемые при получении значения свойства>set < действия, выполняемые при установке значения свойства>>

Вначале определения свойства могут идти различные модификаторы, в частности, модификаторы доступа. Затем указывается тип свойства, после которого идет название свойства. Полное определение свойства содержит два блока: get и set .

В блоке get выполняются действия по получению значения свойства. В этом блоке с помощью оператора return возвращаем некоторое значение.

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

Блоки get и set еще называются акссесорами или методами доступа (к значению свойства), а также геттером и сеттером.

Person person = new Person(); // Устанавливаем свойство - срабатывает блок Set // значение "Tom" и есть передаваемое в свойство value person.Name = "Tom"; // Получаем значение свойства и присваиваем его переменной - срабатывает блок Get string personName = person.Name; Console.WriteLine(personName); // Tom class Person < private string name = "Undefined"; public string Name < get < return name; // возвращаем значение свойства >set < name = value; // устанавливаем новое значение свойства >> >

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

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

А в блоке set устанавливаем значение переменной name. Параметр value представляет передаваемое значение, которое передается переменной name.

В программе мы можем обращаться к этому свойству, как к обычному полю. Если мы ему присваиваем какое-нибудь значение, то срабатывает блок set , а передаваемое значение передается в параметр value :

person.Name = "Tom";

Если мы получаем значение свойства, то срабатывает блок get , который по сути возвращает значение переменной name:

string personName = person.Name;

То есть по сути свойство Name ничего не хранит, оно выступает в роли посредника между внешним кодом и переменной name.

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

Person person = new Person(); Console.WriteLine(person.Age); // 1 // изменяем значение свойства person.Age = 37; Console.WriteLine(person.Age); // 37 // пробуем передать недопустимое значение person.Age = -23; // Возраст должен быть в диапазоне от 1 до 120 Console.WriteLine(person.Age); // 37 - возраст не изменился class Person < int age = 1; public int Age < set < if (value < 1 || value >120) Console.WriteLine("Возраст должен быть в диапазоне от 1 до 120"); else age = value; > get < return age; >> >

В данном случае переменная age хранит возраст пользователя. Напрямую мы не можем обратиться к этой переменной — только через свойство Age. Причем в блоке set мы устанавливаем значение, если оно соответствует некоторому разумному диапазону. Поэтому при передаче свойству Age значения, которое не входит в этот диапазон, значение переменной не будет изменяться:

person.Age = -23;
Консольный вывод программы:

1 37 Возраст должен быть в диапазоне от 1 до 120 37

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

Свойства только для чтения и записи

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

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

Person person = new Person(); // свойство для чтения - можно получить значение Console.WriteLine(person.Name); // Tom // но нельзя установить // person.Name = "Bob"; // ! Ошибка // свойство для записи - можно устновить значение person.Age = 37; // но нелзя получить // Console.WriteLine(person.Age); // ! Ошибка person.Print(); class Person < string name = "Tom"; int age = 1; // свойство только для записи public int Age < set < age = value; >> // свойство только для чтения public string Name < get < return name; >> public void Print()=> Console.WriteLine($"Name: Age: "); >

Здесь свойство Name доступно только для чтения, поскольку оно имеет только блок get :

public string Name < get < return name; >>

Мы можем получить его значение, но НЕ можем установить:

Console.WriteLine(person.Name); // получить можно person.Name = "Bob"; // ! Ошибка - установить нельзя

А свойство Age, наоборот, доступно только для записи, поскольку оно имеет только блок set :

public int Age < set < age = value; >>

Можно установить его значение, но нельзя получить:

person.Age = 37; // установить можно Console.WriteLine(person.Age); // ! Ошибка - получить значение нельзя

Вычисляемые свойства

Свойства необязательно связаны с определенной переменной. Они могут вычисляться на основе различных выражений

Person tom = new("Tom", "Smith"); Console.WriteLine(tom.Name); // Tom Smith class Person < string firstName; string lastName; public string Name < get < return $""; > > public Person(string firstName, string lastName) < this.firstName = firstName; this.lastName = lastName; >>

В данном случае класс Person имеет свойство Name, которое доступно только для чтения и которое возвращает общее значение на основе значений переменных firstName и lastName.

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

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

Person tom = new("Tom"); // Ошибка - set объявлен с модификатором private //tom.Name = "Bob"; Console.WriteLine(tom.Name); // Tom class Person < string name = ""; public string Name < get < return name; >private set < name = value; >> public Person(string name) => Name = name; >

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

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

  • Модификатор для блока set или get можно установить, если свойство имеет оба блока (и set, и get)
  • Только один блок set или get может иметь модификатор доступа, но не оба сразу
  • Модификатор доступа блока set или get должен быть более ограничивающим, чем модификатор доступа свойства. Например, если свойство имеет модификатор public, то блок set/get может иметь только модификаторы protected internal, internal, protected, private 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; >>

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

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

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

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

Person tom = new(); Console.WriteLine(tom.Name); // Tom Console.WriteLine(tom.Age); // 37 class Person < public string Name < get; set; >= "Tom"; public int Age < get; set; >= 37; >

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

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

class Person < public string Name < private set; get;>public Person(string name) => Name = name; >

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

class Person < // через инициализацию свойства public string Name < get; >= "Tom"; // через конструктор public Person(string name) => Name = name; >

Блок init

Начиная с версии C# 9.0 сеттеры в свойствах могут определяться с помощью оператора init (от слова «инициализация» — это есть блок init призван инициализировать свойство). Для установки значений свойств с init можно использовать только инициализатор, либо конструктор, либо при объявлении указать для него значение. После инициализации значений подобных свойств их значения изменить нельзя — они доступны только для чтения. В этом плане init-свойства сближаются со свойствами для чтения. Разница состоит в том, что init-свойства мы также можем установить в инициализаторе (свойства для чтения установить в инициализаторе нельзя). Например:

Person person = new(); //person.Name = "Bob"; //! Ошибка - после инициализации изменить значение нельзя Console.WriteLine(person.Name); // Undefined public class Person < public string Name < get; init; >= "Undefined"; >

В данном случае класс Person для свойства Name вместо сеттера использует оператор init . В итоге на строке

Person person = new();

предполагается создание объекта с инициализацией всех его свойств. В данном случае свойство Name получит в качестве значения строку «Undefined». Однако поскольку инициализация свойства уже произошла, то на строке

person.Name = "Bob"; // Ошибка

мы получим ошибку.

Как можно установить подобное свойство? Выше продемонстрирован один из способов — установка значения при определении свойства. Второй способ — через конструктор:

Person person = new("Tom"); Console.WriteLine(person.Name); // Tom public class Person < public Person(string name) =>Name = name; public string Name < get; init; >>

Третий способ — через инициализатор:

Person person = new() < Name = "Bob">; Console.WriteLine(person.Name); // Bob public class Person < public string Name < get; init; >= ""; >

В принцпе есть еще четвертый способ — установка через другое свойство с модификатором init :

var person = new Person() < Name = "Sam" >; Console.WriteLine(person.Name); // Sam Console.WriteLine(person.Email); // Sam@gmail.com public class Person < string name = ""; public string Name < get < return name; >init < name = value; Email = $"@gmail.com"; > > public string Email < get; init; >= ""; >

В данном случае свойство Name управляет полем для чтения name . Благодаря этому перед установкой значения свойства мы можем произвести некоторую предобработку. Кроме того, в выражении init устанавливается другое init-свойство — Email, которое для установки значения использует значение свойства Name — из имени получаем значение для электронного адреса.

Причем если при объявлении свойства указано значение, то в конструкторе мы можем его изменить. Значение, установленное в конструкторе, можно изменить в инициализаторе. Однако дальше процесс инициализации заканчивается. И значение не может быть изменено.

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

Как и методы, мы можем сокращать определения свойств. Поскольку блоки get и set представляют специальные методы, то как и обычные методы, если они содержат одну инструкцию, то мы их можем сократить с помощью оператора => :

class Person < string name; public string Name < get =>name; set => name = value; > >

Также можно сокращать все свойство в целом:

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

модификатор required

Модификатор required (добавлен в C# 11) указывает, что поле или свойства с этим модификатором обязательно должны быть инициализированы. Например, в следующем примере мы получим ошибку:

Person tom = new Person(); // ошибка - свойства Name и Age не инициализированы public class Person < public required string Name < get; set; >public required int Age < get; set; >>

Здесь свойства Name и Age отмечены как обязательные для инициализации с помощью модификатора required , поэтому необходимо использовать инициализатор для их инициализации:

Person tom = new Person < Name = "Tom", Age = 38 >; // ошибки нет

Причем не важно, устанавливаем эти свойства в конструкторе или инициализируем при определении, все равно надо использовать инициализатор для установки их значений. Например, в следующем примере мы получим ошибку:

Person bob = new Person("Bob"); // ошибка - свойства Name и Age все равно надо установить в инициализаторе public class Person < public Person(string name) < Name = name; >public required string Name < get; set; >public required int Age < get; set; >= 22; >

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *