Что такое init-statement и где оно используется?
Что такое init-statement ? Недавно прочитал в интернете про if с инициализатором. По данным этого же сайта прочитал, что у цикла for так, же на первом месте в скобках стоит init-statement . Я понимаю, что это как обычное объявление, но почему-то для этого существует отдельный термин. Я очень бы хотел разобраться в терминологии и узнать где еще используется init-statement , заранее спасибо.
Отслеживать
28.8k 12 12 золотых знаков 59 59 серебряных знаков 118 118 бронзовых знаков
задан 13 мар 2021 в 19:39
n 1 k z z z n 1 k z z z
1,491 4 4 серебряных знака 21 21 бронзовый знак
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Это не обязательно объявление. Например for (i = 42; /*. */; /*. */) разрешено, хотя i = 42 — это выражение, а не объявление.
init-statement — это термин грамматики, означающий выражение (после которого стоит ; ) или объявление.
Убедимся в этом. Открываем грамматику и видим:
init-statement: expression-statement simple-declaration
Это значит, что init-statement — это или expression-statement , или simple-declaration .
expression-statement: expressionopt ;
expression-statement — это выражение ( expression ), справа от которого стоит точка с запятой. opt (= «optional» = «опционально») значит, что выражения может и не быть, т.е. сама по себе ; — это тоже expression-statement .
simple-declaration — это обычное объявление 1 или нескольких переменных. Его описание в грамматике сложное, поэтому показывать не буду. Интересно, что определить структуру здесь тоже можно: for (struct A < /*. */ >; /*. */; /*. */) , ведь это считается объявлением (объявляющим 0 переменных).
Общие сведения об объявлениях
Объявление задает интерпретацию и атрибуты набора идентификаторов. Объявление, которое также вызывает резервирование хранилища для объекта или функции, именуемой идентификатором, называется «определением». Объявления в C для переменных, функций и типов имеют следующий синтаксис:
Синтаксис
declaration :
declaration-specifiers attribute-seq opt init-declarator-list opt ;
/* attribute-seq необ. относится только к продуктам Майкрософт */
declaration-specifiers :
storage-class-specifier declaration-specifiers необ.
type-specifier declaration-specifiers необ.
type-qualifier declaration-specifiers необ.
init-declarator-list :
init-declarator
init-declarator-list , init-declarator
init-declarator :
declarator
declarator = initializer
Этот синтаксис для объявления ( declaration ) не будет повторяться в следующих разделах. Синтаксис в следующих разделах обычно начинается с нетерминального декларатора ( declarator ).
Объявления в init-declarator-list содержат именуемые идентификаторы, а сокращение init означает инициализатор. init-declarator-list — это последовательность операторов объявления, разделенных запятыми. Каждый из них может содержать дополнительную информацию о типе и (или) инициализатор. В деклараторе ( declarator ) содержатся объявляемые идентификаторы (при наличии). Нетерминальные описатели declaration-specifiers состоят из последовательности спецификаторов типа и класса хранения, которые указывают компоновку, время хранения и по меньшей мере часть типа сущностей, обозначаемых операторами объявления. Объявления состоят из произвольного сочетания описателей класса хранения, описателей типов, квалификаторов типов, операторов объявлений и инициализаторов.
Объявления могут содержать один или несколько из необязательных атрибутов, перечисленных в параметре attribute-seq , а сокращение seq означает последовательность. Эти атрибуты, используемые только в системах Майкрософт, выполняют несколько функций, которые рассматриваются в этой книге.
В общей форме объявления переменных в параметре type-specifier задается тип данных для переменной. type-specifier может быть составным, например иметь модификаторы const и volatile . В параметре declarator содержится имя переменной, которое может быть изменено для объявления массива или типа указателя. Например, примененная к объекту директива
int const *fp;
В приведенном выше примере переменная с именем fp объявляется как указатель на неизменяемое ( const ) значение int . В объявлении можно определить несколько переменных; для этого используется несколько деклараторов, которые разделяются запятыми.
Объявление должно содержать по меньшей мере один декларатор, или его спецификатор типа должен объявлять тег структуры, тег объединения или члены перечисления. Деклараторы предоставляют всю остальную информацию об идентификаторе. Оператор объявления — это идентификатор, который можно изменить с помощью квадратных скобок ( [ ] ), звездочек ( * ) или круглых скобок ( ( ) ) для объявления типов массива, указателя или функции, соответственно. При объявлении простых переменных (например символьных, целочисленных или с плавающей запятой) или структур и объединений простых переменных декларатор ( declarator ) представляет собой только идентификатор. Дополнительные сведения о деклараторах см. в статье Деклараторы и объявления переменных.
Все определения неявным образом являются объявлениями, но не все объявления являются определениями. Например, объявления переменных, использующие extern описатель класса хранения, являются «ссылающимися», а не «определяющими» объявлениями. Если сослаться на внешнюю переменную необходимо до ее определения или если она определена в другом исходном файле, а не в том, в котором она используется, то объявление extern является обязательным. Хранилище не выделяется с помощью «ссылок» объявлений, а переменные не могут быть инициализированы в объявлениях.
В объявлениях переменных должен быть указан класс хранения или тип (или и то, и другое). За исключением ключевого слова __declspec в объявлениях допускается только один спецификатор класса хранения. Некоторые спецификаторы класса хранения не допускаются в некоторых контекстах. Класс хранения __declspec может использоваться с другими описателями класса хранения; его можно указывать более одного раза. Спецификатор класса хранения для объявления влияет только на то, каким образом объявленный элемент хранится и инициализируется, и какие части программы могут ссылаться на элемент.
В C определены терминальные описатели storage-class-specifier auto , extern , register , static и typedef . Кроме того, Microsoft C включает терминальный описатель storage-class-specifier __declspec . Все терминалы storage-class-specifier , за исключением typedef и __declspec , рассматриваются в статье Классы хранения в C. Сведения о typedef см. в статье Декларации typedef . Сведения о __declspec см. в статье Расширенные атрибуты классов хранения в C.
Местоположение объявления в исходном коде программы, а также наличие и отсутствие других объявлений переменной — это факторы, которые имеют значения для определения срока жизни переменных. Возможно несколько повторных объявлений, однако определение может создаваться только один раз. Однако определение может использоваться в нескольких блоках трансляции. Для объектов с внутренней компоновкой это правило применяется отдельно для каждого блока трансляции, поскольку объекты с внутренней компоновкой уникальны для блока трансляции. Для объектов с внешней компоновкой это правило применяется ко всей программе. Дополнительные сведения о видимости см. в статье Время жизни, область, видимость и компоновка.
Спецификаторы типа предоставляют некоторые сведения о типах данных в идентификаторах. По умолчанию используется спецификатор типа int . Дополнительные сведения см. в разделе Спецификаторы типа. Спецификаторы типа также могут определять теги типа, имена компонентов структуры и объединения, а также перечисления константы перечисления. Дополнительные сведения см. в статьях Объявления перечислений C, Объявления структур и Объявления объединений.
Есть два типа терминальных описателей type-qualifier : const и volatile . Эти квалификаторы задают дополнительные свойства типов, которые актуальны только при доступе к объектам этого типа через l-значения. Дополнительные сведения о ключевых словах const и volatile см. в статье Квалификаторы типов. Определение l-значений см. в разделе Выражения L-Value и R-Value.
init (справочник по C#)
Ключевое слово init определяет метод доступа в свойстве или индексаторе. Метод задания только для инициализации назначает значение свойству или элементу индексатора только во время построения объекта. Принудительное init применение неизменяемости, поэтому после инициализации объекта его нельзя изменить. Метод init доступа позволяет вызывать код для использования инициализатора объектов для задания начального значения. В отличие от этого, автоматически реализованное свойство с параметром get задания должно быть инициализировано путем вызова конструктора. Свойство с методом private set доступа можно изменить после построения, но только в классе.
В приведенном ниже примере определен как метод доступа get , так и метод доступа init для свойства с именем YearOfBirth . Для возвращения значения свойства в нем используется закрытое поле с именем _yearOfBirth .
class Person_InitExample < private int _yearOfBirth; public int YearOfBirth < get < return _yearOfBirth; >init < _yearOfBirth = value; >> >
Метод доступа init часто состоит из одного оператора, который присваивает значение, как в предыдущем примере. init Из-за этого не работает следующее :
var john = new Person_InitExample < YearOfBirth = 1984 >; john.YearOfBirth = 1926; //Not allowed, as its value can only be set once in the constructor
Метод init доступа не принудительно задает свойство вызывающим абонентам. Вместо этого он позволяет вызывающим пользователям использовать инициализатор объектов при запрете последующего изменения. Модификатор можно добавить required для принудительного задания свойства вызывающим. В следующем примере показано init только свойство с типом значения NULL в качестве резервного поля. Если вызывающий объект не инициализирует YearOfBirth свойство, это свойство будет иметь значение по умолчанию null :
class Person_InitExampleNullability < private int? _yearOfBirth; public int? YearOfBirth < get =>_yearOfBirth; init => _yearOfBirth = value; > >
Чтобы принудительно задать начальное значение, отличное от NULL, добавьте required модификатор, как показано в следующем примере:
class Person_InitExampleNonNull < private int _yearOfBirth; public required int YearOfBirth < get =>_yearOfBirth; init => _yearOfBirth = value; > >
Метод init доступа можно использовать в качестве элемента с выражением. Пример:
class Person_InitExampleExpressionBodied < private int _yearOfBirth; public int YearOfBirth < get =>_yearOfBirth; init => _yearOfBirth = value; > >
Метод init доступа также можно использовать в свойствах автоимплирования, как показано в следующем примере кода:
class Person_InitExampleAutoProperty < public int YearOfBirth < get; init; >>
В следующем примере показано различие только для private set чтения и init свойств. Приватный набор версии и единственная версия для чтения требуют, чтобы вызывающие пользователи использовали добавленный конструктор для задания свойства name. Версия private set позволяет пользователю изменить свое имя после создания экземпляра. Версия init не требует конструктора. Вызывающие объекты могут инициализировать свойства с помощью инициализатора объектов:
class PersonPrivateSet < public string FirstName < get; private set; >public string LastName < get; private set; >public PersonPrivateSet(string first, string last) => (FirstName, LastName) = (first, last); public void ChangeName(string first, string last) => (FirstName, LastName) = (first, last); > class PersonReadOnly < public string FirstName < get; >public string LastName < get; >public PersonReadOnly(string first, string last) => (FirstName, LastName) = (first, last); > class PersonInit < public string FirstName < get; init; >public string LastName < get; init; >>
PersonPrivateSet personPrivateSet = new("Bill", "Gates"); PersonReadOnly personReadOnly = new("Bill", "Gates"); PersonInit personInit = new() < FirstName = "Bill", LastName = "Gates" >;
Спецификация языка C#
Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.
См. также
- Справочник по C#
- Руководство по программированию на C#
- Ключевые слова в C#
- Свойства
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Инициализация и протоколы — Python: Введение в ООП
Теперь, когда вы знакомы со связанными методами, настало время рассказать про самый важный метод в Python: метод __init__ («dunder-init», «дандер инит»). Этот метод отвечает за инициализацию экземпляров класса после их создания.
На прошлых уроках Бобу — экземпляру класса Person , мы задавали имя уже после того, как сам объект был создан. Такое заполнение атрибутов объекта и выглядит громоздко и может приводить к разного рода ошибкам: объект может какое-то время находиться в «недозаполненном» (более общее название — «неконсистентном») состоянии. Инициализатор же позволяет получить уже полностью настроенный экземпляр.
Реализуем класс Person так, чтобы имя можно было указывать при создании объекта:
class Person: def __init__(self, name): self.name = name bob = Person('Bob') bob.name # 'Bob' alice = Person('Alice') alice.name # 'Alice'
Теперь нет нужды иметь в классе атрибут name = ‘Noname’ , ведь все объекты получают имена при инстанцировании!
Методы и протоколы
Вы заметили, что Python сам вызывает метод __init__ в нужный момент? Это же касается большинства dunder-методов: таковые вы только объявляете, но вручную не вызываете. Такое поведение часто называют протоколом (или поведением): класс реализует некий протокол, предоставляя нужные dunder-методы. А Python, в свою очередь, работает с объектом посредством протокола.
Продемонстрирую протокол получения длины имени объекта:
class Person: def __init__(self, name): self.name = name def __len__(self): return len(self.name) tom = Person('Thomas') len(tom) # 6
Я объявил метод dunder-len, и объекты класса Person научились возвращать «свою длину» (да, пример надуманный, но суть отражает). Вызов функции len на самом деле приводит к вызову метода __len__ у переданного в аргументе объекта!
Протоколы и «утиная типизация»
Существует такой термин: «утиная типизация» («duck typing») — «Если что-то крякает как утка и плавает как утка, то возможно это утка и есть!». Данный термин, пусть и звучит как шутка, описывает важный принцип построения абстракций: коду практически всегда достаточно знать о значении, с которым этот код работает, что это значение обладает нужными свойствами. Все остальное — не существенно.
Если коду достаточно знать, что сущность умеет плавать и крякать, то код не должен проверять сущность на фактическую принадлежность к уткам. Это позволит коду работать с робо-утками, даже если все настоящие утки вымрут, ведь код работает с абстрактными утками!
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях: