Init c что это

от admin

Name already in use

docs / docs / csharp / language-reference / keywords / init.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

init (C# Reference)

In C# 9 and later, the init keyword defines an accessor method in a property or indexer. An init-only setter assigns a value to the property or the indexer element only during object construction. This enforces immutability, so that once the object is initialized, it can’t be changed again.

The following example defines both a get and an init accessor for a property named YearOfBirth . It uses a private field named _yearOfBirth to back the property value.

Often, the init accessor consists of a single statement that assigns a value, as it did in the previous example. Note that, because of init , the following will not work:

The init accessor can be used as an expression-bodied member. Example:

The init accessor can also be used in auto-implemented properties as the following example code demonstrates:

Что нового в C# 9.0

В преддверии старта нового потока курса «C#-разработчик» представляем вашему вниманию обзор нововведений. Среди них — новый метод доступа к свойству — init, не позволяющий изменять свойства после инициализации, with-выражения для изменения свойств объекта прямо здесь и сейчас, записи и новые возможности сопоставления шаблонов. Подробности, конечно же, под катом.

Это официально: вышел C# 9.0! Еще в мае я написал в блоге о планах C# 9.0, а ниже — обновлённая версия этого поста, соответствующая тому, что мы сделали по факту. С каждой новой версией C# мы стремимся к большей ясности и простоте в обычных ситуациях кодирования, и C# 9.0 — не исключение. На этот раз особое внимание уделяется поддержке краткого и иммутабельного представления форм данных.

Свойства только для инициализации

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

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

Единственное большое ограничение на сегодня: для работы инициализаторов объектов свойства должны быть мутабельными: они функционируют, сначала вызывая конструктор объекта (по умолчанию, без параметров в данном случае), а затем присваивая его сеттерам свойств. Свойства только для инициализации исправляют ситуацию! Они вводят метод доступа init , то есть вариант метода доступа set , который может быть вызван только во время инициализации объекта:

При таком объявлении приведённый выше клиентский код все еще корректен, но любое последующее присвоение свойств FirstName и LastName — это ошибка:

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

Методы доступа init и поля только для чтения

Поскольку методы доступа init могут вызваться только во время инициализации, им разрешено изменять поля заключающего их класса «только для чтения», точно так же, как это можно сделать в конструкторе.

Записи

В основе классического ООП лежит идея о том, что объект обладает сильной идентичностью и инкапсулирует изменчивое состояние, которое развивается с течением времени. C# всегда отлично работал в этом смысле, но иногда вы хотите почти прямо противоположного, и здесь значения по умолчанию C#, как правило, мешают, делая работу крайне трудоёмкой. Если вы хотите, чтобы весь объект был неизменяемым и вел себя как значение, вам следует рассмотреть возможность его объявления как record:

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

With-выражения

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

With-выражения используют синтаксис инициализатора объекта, чтобы указать, что именно отличается в новом объекте от старого объекта. Можно указать несколько свойств. Это выражение работает, фактически копируя полное состояние старого объекта в новое, а затем мутируя его в соответствии с инициализатором объекта. Это означает, что свойства должны иметь метод доступа init или set , затем изменяемый в with-выражении.

Равенство на основе значения

Все объекты наследуют виртуальный метод Equals(object) класса object . Это поведение используется как основа для статического метода Object.Equals(object, object) , когда оба параметра ненулевые. Структуры переопределяют этот метод, чтобы иметь «равенство на основе значений», сравнивая каждое поле структуры и рекурсивно вызывая для полей «Equals». Записи делают то же самое. Это означает, что в соответствии с их «значением» две записи могут быть равны друг другу, не будучи одним и тем же объектом. Например, если мы снова изменим фамилию в Person :

Теперь у нас будет ReferenceEquals(person, originalPerson) — ложь (это не один и тот же объект), но Equals(person, originalPerson) — истина (они имеют одинаковое значение). Наряду с основанным на значении Equals , есть также основанное на значении переопределение GetHashCode() , которое будет работать вместе с ним. Кроме того, записи реализуют интерфейс IEquatable <T> и перегружают операторы == и != , Так что поведение, основанное на значениях, последовательно проявляется во всех этих различных механизмах равенства.

Равенство значений и мутабельность не всегда хорошо сочетаются. Одна из проблем заключается в том, что изменение значений может привести с течением времени к изменению результата GetHashCode() , что неудачно, когда объект хранится в хеш-таблице! Мы не запрещаем мутабельные записи, но и не поощряем их, если вы не продумали последствия!

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

Записи могут наследовать от других записей:

With-выражения и равенство значений хорошо работают вместе с наследованием записей, поскольку они учитывают весь объект среды выполнения, а не только тип, под которым объект известен статически. Скажем, я создаю Student , но сохраняю его в переменной Person:

Выражение with по-прежнему будет копировать весь объект с сохранением типа среды выполнения:

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

Позиционные записи

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

Но существует гораздо более короткий синтаксис для выражения точно того же:

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

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

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

Программы верхнего уровня

Написание простой программы на C# требует значительного количества шаблонного кода:

Это не только ошеломляет новичков в языке, но и загромождает код, добавляя уровни отступов. В C# 9.0 вы можете написать программу на самом верхнем уровне, вот так:

Допускается любое выражение. Программа должна выполняться после «using» и перед любыми объявлениями типа или пространства имен в файле, и это возможно только в одном файле, точно так же, как на данный момент у вас может быть только один метод Main . Допустимы return и await . А если вы хотите получить доступ к аргументам командной строки, то args доступен как «магический» параметр.

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

Улучшено сопоставление шаблонов

В C# 9.0 добавлено несколько новых типов шаблонов. давайте рассмотрим их в контексте этого фрагмента кода из туториала по сопоставлению шаблонов:

Простой шаблон типа

Сейчас, когда тип совпадает, шаблон типа должен объявлять идентификатор – даже если это пустой идентификатор _ , как в DeliveryTruck _ выше. Но в C# 9.0 можно просто написать тип:

Шаблоны отношений

В C# 9.0 введены шаблоны, соответствующие операторам отношений < , <= и так далее. Так что теперь вы можете записать часть DeliveryTruck вышеуказанного шаблона как вложенное выражение switch:

Здесь > 5000 и < 3000 — шаблон отношений.

Логические шаблоны

Наконец, вы можете комбинировать шаблоны с логическими операторами and , or и not , записанными в виде слов, чтобы избежать путаницы с операторами в выражениях. Например, случаи вложенного switch выше можно расположить в порядке возрастания вот так:

В середине and используется для объединения двух реляционных паттернов и формирования паттерна, представляющего интервал. Обычно шаблон not применяется к шаблону констант null , как в not null . Например, мы можем разделить обработку неизвестных случаев в зависимости от того, являются ли они null :

Также not удобен в содержащих is-выражения условиях if вместо громоздких двойных скобок:

Можно написать только это:

Фактически в таком выражении is not мы позволяем вам как-то назвать Customer для дальнейшего использования:

Выражения new и целевой тип

«Целевой тип» — это термин, который мы употребляем, когда выражение получает свой тип из контекста того места, где оно используется. Например, null и лямбда-выражения всегда целевые. Выражения new в C# всегда требовали явного указания типа (кроме неявно типизированных выражений массива). В C # 9.0 можно не указывать тип, если есть чёткий тип, которому присваивается выражение.

Это особенно удобно, когда у вас много повторений, например, в массиве или инициализаторе объекта:

Ковариантные return

Иногда полезно указать, что переопределение метода в производном классе имеет более конкретный возвращаемый тип, чем объявление в базовом типе. C# 9.0 позволяет это сделать:

И многое другое

Узнать о полном наборе функций C# 9.0 можно в документации «Что нового в C# 9.0».
А с промокодом HABR можно получить дополнительные 10 % к скидке, указанной на баннере.

What does init mean in c# 9?

I have come across with «init» keyword in c# in the C# 9 preview. What does it mean and what are its applications?

icn's user avatar

2 Answers 2

The init keyword creates so called Init Only Setters. They add the concept of init only properties and indexers to C#. These properties and indexers can be set at the point of object creation but become effectively get only once object creation has completed.

The main reason for the introduction is to avoid boilerplate code.

A simple immutable object like Point requires:

The init accessor makes immutable objects more flexible by allowing the caller to mutate the members during the act of construction. That means the object’s immutable properties can participate in object initializers and thus removes the need for all constructor boilerplate in the type. The Point type is now simply:

Init c что это

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Блок init

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Читать:
Как применить функцию к нескольким столбцам pandas

Похожие статьи