Вызов в упакованных экземплярах

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

· Когда функция-член является перегрузкой override метода, унаследованного от типа object, и вызывается через выражение экземпляра типа object.

· Когда функция-член является реализацией функции-члена экземпляра и вызывается через выражение экземпляра типа интерфейса.

· Когда функция-член вызывается через делегат.

В таких ситуациях считается, что упакованный экземпляр содержит переменную с типом значения и эта переменная становится переменной, на которую ссылается оператор this в вызове функции-члена. В частности, это означает, что при вызове функции-члена в упакованном экземпляре она может изменять значение, хранящееся в упакованном экземпляре.

Первичные выражения

Первичные выражения включают самые простые формы выражений.

primary-expression:
primary-no-array-creation-expression
array-creation-expression

primary-no-array-creation-expression:
literal
simple-name
parenthesized-expression
member-access
invocation-expression
element-access
this-access
base-access
post-increment-expression
post-decrement-expression
object-creation-expression
delegate-creation-expression
anonymous-object-creation-expression
typeof-expression
checked-expression
unchecked-expression
default-value-expression
anonymous-method-expression

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

object o = new int[3][1];

который в противном случае интерпретировался бы как

object o = (new int[3])[1];

Литералы

Первичное выражение, состоящее из литерала (§2.4.4), считается значением.

Простые имена

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

simple-name:
identifier type-argument-listopt

Простое имя имеет вид I или одну из форм I<A1, ..., AK>, где I — один идентификатор и <A1, ..., AK> — дополнительный список аргументов типа. Если список аргументов типа не указан, K считается равным нулю. Простое имя вычисляется и классифицируется следующим образом.

· Если K равно нулю и простое имя находится внутри блока и область объявления локальных переменных блока (или включающего блока) (§3.3) содержит локальную переменную, параметр или ограничение с именем I, то простое имя относится к этой переменной, параметру или константе и считается переменной или значением.

· Если K равно нулю и простое имя находится внутри тела объявления универсального метода и если это объявление включает параметр типа с именем I, то простое имя относится к этому параметру типа.

· Иначе для каждого типа экземпляра T (§10.3.1), начиная от типа экземпляра в объявлении непосредственного включающего типа и до типа экземпляра в объявлении каждого включающего класса и структуры (если есть)

o Если K равно нулю и объявление T включает параметр типа с именем I, то простое имя относится к этому параметру типа.

o Иначе, если при поиске члена I (§7.4) в T с аргументами типа K получены результаты:

· Если T имеет тип экземпляра непосредственного включающего класса или структуры и при поиске находится один или несколько методов, то результатом является группа методов со связанным выражением экземпляра this. Если указан список аргументов типа, он используется при вызове универсального метода (§7.6.5.1).

· Иначе, если T имеет тип экземпляра непосредственного включающего класса или структуры и при поиске находится член экземпляра и если внутри блока конструктора экземпляра, метода экземпляра или метода доступа к экземпляру находится ссылка, то результатом является метод доступа к члену (§7.6.4) в виде this.I. Это происходит, только когда K равно нулю.

· Иначе результатом является метод доступа к члену (§7.6.4) в виде T.I или T.I<A1, ...,. AK>. В таком случае если простое имя будет относиться к члену экземпляра, это будет вызывать ошибку времени компиляции.

· Иначе для каждого пространства имен N, начиная с пространства имен, в котором находится простое имя, и заканчивая всеми включающими пространствами имен (если есть) и глобальным пространством имен, пока не будет найдена сущность, будут выполняться следующие действия.

o Если K равно нулю и I является именем пространства имен в N, то:

· Если местонахождение простого имени включено в объявление пространства имен для N и объявление пространства имен содержит директиву extern alias или директиву using alias, которая связывает имя I с пространством имен или типом, тогда простое имя неоднозначно и возникает ошибка времени компиляции.

· Иначе простое имя относится к пространству имен I в N.

o Иначе, если N содержит доступный тип с именем I и параметрами типа K, то:

· Если K равно нулю и местонахождение простого имени включено в объявление пространства имен для N и объявление пространства имен содержит директиву extern alias или директиву using alias, которая связывает имя I с пространством имен или типом, тогда простое имя неоднозначно и возникает ошибка времени компиляции.

· Иначе имя пространства имен или типа относится к типу, сформированному с данными аргументами типа.

o Иначе, если местонахождение простого имени включено в объявление пространства имен для N, то:

· Если K равно нулю и объявление пространства имен содержит директиву extern alias или директиву using alias, которая связывает имя I с импортированным пространством имен или типом, тогда простое имя относится к этому пространству имен или типу.

· Иначе, если пространства имен, импортированные с помощью директив using namespace в объявлении пространства имен, содержат ровно один тип с именем I и параметрами типа K, тогда простое имя относится к этому типу, сформированному с данными аргументами типа.

· Иначе, если пространства имен, импортированные с помощью директив using namespace в объявлении пространства имен, содержат более одного типа с именем I и параметрами типа K, тогда простое имя неоднозначно и возникает ошибка.

Обратите внимание, что этот этап полностью повторяет соответствующий этап в обработке имени пространства имен или типа (§3.8).

· Иначе простое имя не определено и возникает ошибка времени компиляции.

7.6.2.1 Инвариантность значения в блоках

Для каждого вхождения данного идентификатора в качестве полного простого имени (без списка аргументов типа) в выражении или деклараторе внутри области объявления локальных переменных (§3.3), непосредственно включающей это вхождение, каждое вхождение того же самого идентификатора в качестве полного простого имени в выражении или декларатора должно ссылаться на одну и ту же сущность. Это правило позволяет гарантировать, что внутри определенного блока, блока switch, а также операторов for, foreach и using имя всегда имеет одинаковое значение.

Пример:

class Test
{
double x;

void F(bool b) {
x = 1.0;
if (b) {
int x;
x = 1;
}
}
}

возникнет ошибка времени компиляции, потому что x относится к другим сущностям во внешнем блоке (часть которого включает вложенный блок в операторе if). Наоборот, в примере

class Test
{
double x;

void F(bool b) {
if (b) {
x = 1.0;
}
else {
int x;
x = 1;
}
}
}

ошибки не возникает, поскольку имя x не используется во внешнем блоке.

Обратите внимание, что правило инвариантности значения применяется только к простым именам. Один идентификатор вполне может иметь одно значение в виде простого имени и другое значение в виде правого операнда в методе доступа к члену (§7.6.4). Пример:

struct Point
{
int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

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

Выражения со скобками

Выражение со скобками состоит из выражения, заключенного в скобки.

parenthesized-expression:
( expression )

Выражение со скобками вычисляется путем вычисления выражения внутри скобок. Если выражение внутри скобок обозначает пространство имен или тип, возникает ошибка времени компиляции. Иначе результатом выражения со скобками является результат вычисления содержащегося выражения.

Доступ к члену

Доступ к члену состоит из первичного выражения, стандартного типа или уточненного члена псевдонима, за которым следует точка «.» и идентификатор. В конце может находиться список аргументов типа.

member-access:
primary-expression . identifier type-argument-listopt
predefined-type . identifier type-argument-listopt
qualified-alias-member . identifier type-argument-listopt

predefined-type: one of
bool byte char decimal double float int long
object sbyte short string uint ulong ushort

Создание уточненного члена псевдонима определяется в разделе §9.7.

Доступ к члену имеет вид E.I или одну из форм E.I<A1, ..., AK>, где E — первичное выражение, I — один идентификатор, а <A1, ..., AK> — необязательный список аргументов типа. Если список аргументов типа не указан, K считается равным нулю.

Доступ к члену с первичным выражением типа dynamic привязывается динамически (§7.2.2). В этом случае компилятор классифицирует доступ к члену как доступ к свойству типа dynamic. Представленные ниже правила определения значения доступа к члену затем применяются во время выполнения, используя тип времени выполнения, а не тип времени компиляции первичного выражения. Если класс времени выполнения ведет к группе методов, то метод доступа к члену должен являться первичным выражением для выражения вызова.

Результат метода доступа к члену вычисляется и классифицируется следующим образом.

· Если K равно нулю, E является пространством имен и E содержит вложенное пространство имен I, то результатом является это пространство имен.

· Иначе, если E является пространством имен, E содержит доступный тип с именем I и параметрами типа K, то результатом является этот тип, сформированный с указанными аргументами типа.

· Если E является стандартным типом или первичным выражением, которое классифицируется как тип, и если E не является параметром типа и если при поиске члена I (§7.4) в E с параметрами типа K было найдено соответствие, то E.I вычисляется и классифицируется следующим образом.

o Если I обозначает тип, то результатом является этот тип, сформированный с указанными аргументами типа.

o Если I обозначает один или несколько методов, то результатом является группа методов без связанного выражения экземпляра. Если указан список аргументов типа, он используется при вызове универсального метода (§7.6.5.1).

o Если I обозначает свойство типа static, то результатом является метод доступа к свойству без связанного выражения экземпляра.

o Если I определяет поле static:

· Если поле предназначено только для чтения (readonly) и вне статического конструктора класса или структуры, в которой объявляется поле, имеется ссылка, то результатом является значение, а именно значение статического поля I в E.

· Иначе результатом является переменная, а именно статическое поле I в E.

o Если I определяет событие static:

· Если внутри класса или структуры, в которой объявляется событие, имеется ссылка и это событие было объявлено без объявлений метода доступа к событию (§10.8), то E.I обрабатывается точно так же, как если бы I было статическим полем.

· Иначе результатом является метод доступа к событию без связанного выражения экземпляра.

o Если I обозначает константу, то результатом является значение, а именно значение этой константы.

o Если I обозначает член перечисления, то результатом является значение, а именно значение этого члена перечисления.

o Иначе E.I является недопустимой ссылкой на член, и возникает ошибка времени компиляции.

· Если E является методом доступа к свойству или индексатору, переменной или значением с типом T и при поиске члена I (§7.4) в T с аргументами типа K было найдено соответствие, то E.I вычисляется и классифицируется следующим образом.

o Во-первых, если E является свойством или методом доступа к индексатору, то происходит получение значения свойства или метода доступа к индексатору (§7.1.1) и класс E меняется на значение.

o Если I обозначает один или несколько методов, то результатом является группа методов со связанным выражением экземпляра E. Если указан список аргументов типа, он используется при вызове универсального метода (§7.6.5.1).

o Если I обозначает свойство экземпляра, то результатом является метод доступа к свойству со связанным выражением экземпляра E.

o Если T является типом класса и I обозначает поле экземпляра этого типа класса, то

· Если значение E равно null, возникает исключение System.NullReferenceException.

· Иначе, если поле предназначено только для чтения (readonly) и вне статического конструктора класса или структуры, в которой объявляется поле, имеется ссылка, то результатом является значение, а именно значение поля I в объекте, на который ссылается E.

· Иначе результатом является переменная, а именно поле I в объекте, на который ссылается E.

o Если T является типом структуры и I обозначает поле экземпляра этого типа структуры, то

· Если E является значением или если поле предназначено только для чтения (readonly) и вне статического конструктора класса или структуры, в которой объявляется поле, имеется ссылка, то результатом является значение, а именно значение поля I в экземпляре структуры, на который ссылается E.

· Иначе результатом является переменная, а именно поле I в экземпляре структуры, предоставленном E.

o Если I обозначает событие экземпляра, то

· Если внутри класса или структуры, в которой объявляется событие, имеется ссылка, данное событие было объявлено без объявлений метода доступа к событию (§10.8) и ссылка не находится в левой части оператора += или -=, то E.I обрабатывается точно так же, как если бы I было полем экземпляра.

· Иначе результатом является метод доступа к событию со связанным выражением экземпляра E.

· Иначе выполняется попытка обработать E.I как вызов метода расширения (§7.6.5.2). Если такой вызов завершается сбоем, E.I является недопустимой ссылкой на член и возникает ошибка времени привязки.

Наши рекомендации