Идентичные простые имена и имена типов

В методе доступа к члену в виде E.I, где E является простым идентификатором, а значение E в качестве простого имени (§7.6.2) является константой, полем, свойством, локальной переменной или параметром того же типа, что и значение E в качестве имени типа (§3.8), то допустимы оба возможных значения E. Два возможных значения E.I никогда не являются неоднозначными, поскольку I в обоих случаях обязательно должен быть членом типа E. Другими словами, это правило просто разрешает доступ к статическим членам и вложенным типам E, когда в противном случае возникла бы ошибка времени компиляции. Пример:

struct Color
{
public static readonly Color White = new Color(...);
public static readonly Color Black = new Color(...);

public Color Complement() {...}
}

class A
{
public Color Color; // Field Color of type Color

void F() {
Color = Color.Black; // References Color.Black static member
Color = Color.Complement(); // Invokes Complement() on Color field
}

static void G() {
Color c = Color.White; // References Color.White static member
}
}

Внутри класса A вхождения идентификатора Color, которые ссылаются на тип Color, подчеркиваются, а которые ссылаются на поле Color, не подчеркиваются.

Грамматическая неоднозначность

Порождения для простого имени (§7.6.2) и доступа к члену (§7.6.4) могут привести к появлению неоднозначности в грамматике выражений. Например, оператор

F(G<A,B>(7));

может интерпретироваться как вызов F с двумя аргументами: G < A и B > (7). Иначе он может интерпретироваться как вызов F с одним аргументом, который представляет собой вызов универсального метода G с двумя аргументами типа и одним обычным аргументом.

Если последовательность лексем (в контексте) можно разобрать как простое имя (§7.6.2), метод доступа к члену (§7.6.4) или метод доступа к указателю члена (§18.5.2), заканчивающийся списком аргументов типа (§4.4.1), то выполняется проверка лексемы, следующей непосредственно за закрывающей лексемой >. Если это одна из лексем

( ) ] } : ; , . ? == != | ^

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

F(G<A,B>(7));

в соответствии с этим правилом будет интерпретироваться как вызов F с одним аргументом, который представляет собой вызов универсального метода G с двумя аргументами типа и одним обычным аргументом. Каждый оператор

F(G < A, B > 7);
F(G < A, B >> 7);

будет интерпретирован как вызов F с двумя аргументами. Оператор

x = F < A > +y;

будет интерпретирован как оператор «меньше чем», оператор «больше чем» и унарный оператор «плюс», как если бы выражение было записано в виде x = (F < A) > (+y), а не в виде простого имени со списком аргументов типа, за которым следует бинарный оператор «плюс». В операторе

x = y is C<T> + z;

лексемы C<T> интерпретируются как имя пространства имен или типа со списком аргументов типа.

Выражения вызова

Выражение вызова используется для вызова метода.

invocation-expression:
primary-expression ( argument-listopt )

Выражение вызова динамически привязано (§7.2.2), если верно по меньшей мере одно из следующих утверждений:

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

В этом случае компилятор классифицирует выражение вызова как значение типа dynamic. Представленные ниже правила определения значения выражения вызова затем применяются во время выполнения, используя тип времени выполнения вместо правил типа времени компиляции первичного выражения и аргументов, имеющих тип времени компиляции dynamic. Если основное выражение не имеет тип времени компиляции dynamic, то для вызова метода будет выполнена ограниченная проверка времени компиляции, как описано в §7.5.4.

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

Необязательный список аргументов (§7.5.1) содержит значения или ссылки на переменные для параметров метода.

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

· Если выражение вызова вызывает метод или делегат, который возвращает значение типа void, то результат отсутствует. Выражение без класса допускается только в контексте выражения оператора (§8.6) или в качестве тела лямбда-выражения (§7.15). Иначе возникает ошибка времени привязки.

· Иначе результатом является значение типа, возвращенного методом или делегатом.

Вызовы методов

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

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

· Формируется набор методов-кандидатов для вызова метода. Для каждого метода F, связанного с группой методов M:

o Если F является неуниверсальным методом, то F является кандидатом, когда:

· у M нет списка аргументов типа и

· F является применимым в соответствии со списком A (§7.5.3.1).

o Если F является универсальным методом, а у M нет списка аргументов типа, то F является кандидатом, когда:

· успешно завершается вывод типа (§7.5.2), предоставляющий список аргументов типа для вызова, и

· после замены выведенных аргументов типа на соответствующие параметры типа для метода все сформированные типы в списке параметров F соответствуют своим ограничениям (§4.4.4), а список параметров F применим в соответствии со списком A (§7.5.3.1).

o Если F является универсальным методом, а M включает список аргументов типа, то F является кандидатом, когда:

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

· после замены аргументов типа на соответствующие параметры типа для метода все сформированные типы в списке параметров F соответствуют своим ограничениям (§4.4.4), а список параметров F применим в соответствии со списком A (§7.5.3.1).

· Набор методов-кандидатов сокращается, и содержи только методы из старших производных типов: для каждого метода C.F в наборе, где C — тип, в котором объявляется метод F, все методы, объявленные в базовом типе C, удаляются из набора. Более того, если C является типом класса, отличным от object, то все методы, объявленные в типе интерфейса из набора удаляются. (Последнее правило применяется только, когда группа методов является результатом поиска члена по параметру типа при наличии действительного базового класса, отличного от object, и непустого действительного набора интерфейсов.)

· Если результирующий набор методов-кандидатов пуст, то дальнейшая обработка вместе со следующими этапами не выполняется, а вместо этого предпринимается попытка обработать вызов в виде вызова метода расширения (§7.6.5.2). Если такая обработка не удается, то применимых методов не существует и возникает ошибка времени привязки.

· Лучший метод из набора методов-кандидатов определяется с помощью правил разрешения перегрузки из раздела §7.5.3. Если определить один лучший метод нельзя, то вызов метода является неоднозначным и возникает ошибка времени привязки. При проведении разрешения перегрузки параметры универсального метода учитываются после замены аргументов типа (предоставленных или выведенных) на соответствующие параметры типа для метода.

· Выполняется последняя проверка выбранного лучшего метода.

o Метод проверяется в контексте группы методов: если наиболее подходящий метод является статическим, группа методов, скорее всего, получилась из простого имени или доступа к члену через тип. Если лучший метод является методом экземпляра, то группа методов должна формироваться на основании простого имени, доступа к члену через переменную или значение или на основании доступа base. Если не выполняется ни одно из этих требований, то возникает ошибка времени привязки.

o Если лучший метод является универсальным методом, то выполняется проверка аргументов типа (предоставленных или выведенных) в соответствии с ограничениями (§4.4.4), объявленными в универсальном методе. Если какой-либо аргумент типа не удовлетворяет соответствующим ограничениям для типа параметра, то возникает ошибка времени привязки.

После выбора и проверки метода в соответствии с указанными этапами на этапе привязки обрабатывается фактический вызов времени выполнения в соответствии с правилами вызова функций-членов из раздела §7.5.4.

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

Вызовы методов расширения

Для вызова метода (§7.5.5.1) одного из видов

expr . identifier ( )

expr . identifier ( args )

expr . identifier < typeargs > ( )

expr . identifier < typeargs > ( args )

если при обычной обработке вызова применимые методы не найдены, то предпринимается попытка обработать конструкцию в виде вызова метода расширения. Если выражение expr или любой из аргументов args имеет тип времени компиляции dynamic, методы расширения применены не будут.

Задачей является найти лучшее имя типа C, чтобы можно было вызвать соответствующий статический метод:

C . identifier ( expr )

C . identifier ( expr , args )

C . identifier < typeargs > ( expr )

C . identifier < typeargs > ( expr , args )

Метод расширения Ci.Mj доступен, если:

· Ci не является универсальным или вложенным классом

· Имя Mj является идентификатором.

· Mj доступен и применим для аргументов как статический метод, как указано выше

· Существует неявная идентификация, ссылка или преобразование упаковки от выражения expr до типа первого параметра Mj.

Поиск C выполняется следующим образом.

· Предпринимаются попытки найти набор кандидатов методов расширения, начиная с ближайшего объявления включающего пространства имен, с каждого включающего объявления пространства имен, и заканчивая включающим скомпилированным модулем.

o Если данное пространство имен или блок компиляции напрямую содержит объявления неуниверсального типа Ci с доступными методами расширения Mj, то набор этих методов является набором кандидатов.

o Если пространства имен, импортированные с помощью директив пространства имен в данном пространстве имен или блоке компиляции, напрямую содержат объявления неуниверсального типа Ci с доступными методами расширения Mj, то набор этих методов является набором кандидатов.

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

· Иначе к набору кандидатов применяется разрешение перегрузки, как описано в разделе (§7.5.3). Если один лучший метод не найден, то возникает ошибка времени компиляции.

· C — это тип, в котором лучший метод объявляется в качестве метода расширения.

После этого с помощью C выполняется вызов метода в виде вызова статического метода (§7.5.4).

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

public static class E
{
public static void F(this object obj, int i) { }

public static void F(this object obj, string s) { }
}

class A { }

class B
{
public void F(int i) { }
}

class C
{
public void F(object obj) { }
}

class X
{
static void Test(A a, B b, C c) {
a.F(1); // E.F(object, int)
a.F("hello"); // E.F(object, string)

b.F(1); // B.F(int)
b.F("hello"); // E.F(object, string)

c.F(1); // C.F(object)
c.F("hello"); // C.F(object)
}
}

В этом примере метод B имеет приоритет по сравнению с первым методом расширения, а метод C имеет приоритет над обоими методами расширения.

public static class C
{
public static void F(this int i) { Console.WriteLine("C.F({0})", i); }
public static void G(this int i) { Console.WriteLine("C.G({0})", i); }
public static void H(this int i) { Console.WriteLine("C.H({0})", i); }
}

namespace N1
{
public static class D
{
public static void F(this int i) { Console.WriteLine("D.F({0})", i); }
public static void G(this int i) { Console.WriteLine("D.G({0})", i); }
}
}

namespace N2
{
using N1;

public static class E
{
public static void F(this int i) { Console.WriteLine("E.F({0})", i); }
}

class Test
{
static void Main(string[] args)
{
1.F();
2.G();
3.H();
}
}
}

Вывод в данном примере имеет вид:

E.F(1)
D.G(2)
C.H(3)

D.G имеет приоритет над C.G, а E.F имеет приоритет над D.F и C.F.

Вызовы делегатов

Для вызова метода основное выражение для выражения вызова должно быть значением типа делегата. Кроме того, учитывая, что тип делегата должен быть функцией-членом с таким же списком параметров, что и тип делегата, он должен быть применим (§7.5.3.1) в соответствии со списком аргументов выражения вызова.

Во время выполнения обработка вызова делегата в виде D(A), где D является основным выражением типа делегата, а A является необязательным списком аргументов, включает следующие этапы.

· Вычисляется D. Если при этом вычислении возникает исключение, дальнейшие этапы не выполняются.

· Выполняется проверка допустимости значения D. Если D имеет значение null, вызывается исключение System.NullReferenceException и дальнейшие действия не выполняются.

· Иначе D является ссылкой на экземпляр делегата. Вызовы функций-членов (§7.5.4) выполняются для каждой вызываемой сущности в списке вызова делегата. Для вызываемых сущностей, состоящих из экземпляра и метода экземпляра, вызываемый экземпляр является экземпляром, содержащимся в вызываемой сущности.

Метод доступа к элементу

Доступ к элементу состоит из первичного выражения создания не массива, за которым следует лексема "[" и список аргументов, за которым следует лексема "]". Список аргументов содержит один или несколько аргументов, разделенных запятыми.

element-access:
primary-no-array-creation-expression [ argument-list ]

Список аргументов метода доступа к элементу не должен содержать аргументы ref или out.

Доступ к элементу динамически привязан (§7.2.2), если верно по меньшей мере одно из следующих утверждений:

  • первичное выражение создания не массива имеет тип времени компиляции dynamic;
  • по меньшей мере одно выражение списка аргументов имеет тип времени выполнения dynamic, а первичное выражение создания не массива не имеет типа массива.

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

Если основное выражение отличное от создания массива, для метода доступа к элементу является значением с типом массива, то метод доступа к элементу является методом доступа к массиву (§7.6.6.1). Иначе основное выражение отличное от создания массива, должно быть переменной или значением типа класса, структуры или интерфейса с одним или несколькими членами индексатора, и в таком случае метод доступа к элементу является методом доступа к индексатору (§7.6.6.2).

Доступ к массиву

При доступе к массиву основное выражение отличное от создания массива, для метода доступа к элементу должно быть значением типа массива. В дальнейшем список аргументов метода доступа к массиву не должен содержать именованные аргументы. Число выражений в списке аргументов должно быть идентичным рангу типа массива, а каждое выражение должно иметь тип int, uint, long, ulong или должно подразумевать возможность преобразования в один или несколько этих типов.

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

Обработка времени выполнения доступа к массиву в виде P[A], где P является первичным выражением создания не массива с типом массива, а A является списком аргументов, включает следующие этапы.

· Вычисляется P. Если при этом вычислении возникает исключение, дальнейшие этапы не выполняются.

· Вычисляются выражения индекса в списке аргументов по порядку слева направо. После вычисления всех выражений выполняется неявное преобразование (§6.1) в один из следующих типов:int, uint, long, ulong. Выбирается первый тип из этого списка, для которого существует неявное преобразование. Например, если выражение индекса имеет тип short, выполняется неявное преобразование в тип int, поскольку возможны неявные преобразования из short в int и из short в long. Если при вычислении выражения индекса или последующем неявном преобразовании возникает исключение, то следующие выражения индекса не вычисляются и дальнейшие этапы не выполняются.

· Выполняется проверка допустимости значения P. Если P имеет значение null, вызывается исключение System.NullReferenceException и дальнейшие действия не выполняются.

· Значение каждого выражения в списке аргументов проверяется на соответствие фактическим границам каждого измерения экземпляра массива, на который ссылается P. Если одно или несколько значений выходят за пределы допустимого диапазона, вызывается исключение System.IndexOutOfRangeException и дальнейшие этапы не выполняются.

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

Доступ к индексатору

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

Обработка времени привязки доступа к индексатору в виде P[A], где P является первичным выражением создания не массива с типом класса, структуры или интерфейса T, а A является списком аргументов, включает следующие этапы.

· Создается набор индексаторов, предоставляемых T. Этот набор состоит из всех индексаторов, объявленных в T или в базовом для T типе, которые не являются перегруженными объявлениями (override) и доступны в текущем контексте (§3.5).

· Этот набор сокращается до индексаторов, которые применимы и не скрыты другими индексаторами. К каждому индексатору S.I в наборе, где S является типом, в котором определен I, применяются следующие правила.

o Если I не применим в соответствии со списком A (§7.5.3.1), то I удаляется из набора.

o Если I применим в соответствии со списком A (§7.5.3.1), то все индексаторы, объявленные в базовом типе для S удаляются из набора.

o Если I применим в соответствии со списком A (§7.5.3.1) и S имеет тип класса, отличный от object, то из набора удаляются все индексаторы, объявленные в интерфейсе.

· Если результирующий набор индексаторов-кандидатов пуст, то применимых индексаторов нет и возникает ошибка времени привязки.

· Лучший индексатор из набора индексаторов-кандидатов определяется с помощью правил разрешения перегрузки из раздела §7.5.3. Если определить один лучший индексатор нельзя, то метод доступа к индексатору является неоднозначным и возникает ошибка времени привязки.

· Вычисляются выражения индекса в списке аргументов по порядку слева направо. Результатом обработки доступа к индексатору является выражение, классифицированное как доступ к индексатору. Выражение доступа к индексатору ссылается на индексатор, определенный на предыдущем этапе, и имеет связанное выражение экземпляра P и связанный список аргументов A.

В зависимости от контекста использования доступ к индексатору задает вызов метода доступа get или метода доступа set индексатора. Если доступ к индексатору является назначением присваивания, то для назначения нового значения вызывается метод доступа set (§7.17.1). Во всех остальных случаях для получения текущего значения вызывается метод доступа get (§7.1.1).

Доступ this

Доступ this представляет собой зарезервированное слово this.

this-access:
this

Доступ this допустим только в блоке конструктора экземпляра, метода экземпляра или метода доступа к экземпляру. Он имеет одно из следующих значений.

· Когда доступ this используется в основном выражении внутри конструктора экземпляра класса, он классифицируется как значение. Типом значения является тип экземпляра (§10.3.1) класса, внутри которого происходит это использование, а значением является ссылка на создаваемый объект.

· Когда доступ this используется в основном выражении внутри метода экземпляра или метода доступа к экземпляру, он классифицируется как значение. Типом значения является тип экземпляра (§10.3.1) класса, внутри которого возникает это использование, а значением является ссылка на объект, для которого вызывается метод или метод доступа.

· Когда доступ this используется в основном выражении внутри конструктора экземпляра структуры, он классифицируется как значение. Типом значения является тип экземпляра (§10.3.1) структуры, внутри которой происходит это использование, а значением является создаваемая структура. Переменная this конструктора экземпляра структуры действует точно так же, как параметр out типа структуры, в частности, это означает, что переменная должна явно назначаться в каждом пути выполнения конструктора экземпляра.

· Когда доступ this используется в основном выражении внутри метода экземпляра или метода доступа к экземпляру структуры, он классифицируется как значение. Типом переменной является тип экземпляра структуры (§10.3.1), внутри которой происходит это использование.

o Если метод или метод доступа не является итератором (§10.14), то переменная this представляет структуру, для которой был вызван метод или метод доступа, и действует точно так же, как параметр ref типа структуры.

o Если метод или метод доступа является итератором, то переменная this представляет копию структуры, для которой был вызван метод или метод доступа, и действует точно так же, как параметр value типа структуры.

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

Доступ base

Доступ base состоит из зарезервированного слова base, за которым следует либо лексема "." с идентификатором, либо список аргументов, заключенный в квадратные скобки:

base-access:
base . identifier
base [ argument-list ]

Доступ base используется для доступа к членам базового класса, скрытым членами с такими же именами в текущем классе или структуре. Доступ base допустим только в блоке конструктора экземпляра, метода экземпляра или метода доступа к экземпляру. Когда вызов base.I происходит в классе или структуре, I должно обозначать член базового класса для данного класса или структуры. Так же, когда вызов base[E] оказывается в классе, в базовом классе должен существовать применимый индексатор.

Во время привязки выражения доступа base в виде base.I и base[E] вычисляются точно так же, как если бы они записывались в виде ((B)this).I и ((B)this)[E], где B является базовым классом для класса или структуры, в которой находится эта конструкция. Таким образом, base.I и base[E] соответствуют выражениям this.I и this[E] за исключением того, что this рассматривается как экземпляр базового класса.

Когда в доступе base оказывается ссылка на виртуальную функцию-член (метод, свойство или индексатор), способ определения вызываемой во время выполнения функции-члена (§7.5.4) меняется. Вызываемая функция-член определяется путем поиска наиболее производной реализации (§10.6.3) функции-члена в соответствии с B (вместо сравнения с типом this во время выполнения, что является обычной процедурой при доступе, отличном от base). Таким образом, внутри виртуальной (virtual) или перегруженной (override) функции-члена доступ base можно использовать для вызова наследованной реализации функции-члена. Если функция-член, на которую ссылается доступ base, является абстрактной, то возникает ошибка времени привязки.

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