Перегрузка в универсальных классах
Несмотря на то что сигнатуры при объявлении должны быть уникальными, при замене аргументов типа могут создаваться идентичные сигнатуры. В таких случаях конечные правила разрешения перегрузки, описанные выше, позволят выбрать конкретный член.
В следующих примерах показываются допустимые и недопустимые в соответствии с этим правилом перегрузки:
interface I1<T> {...}
interface I2<T> {...}
class G1<U>
{
int F1(U u); // Overload resulotion for G<int>.F1
int F1(int i); // will pick non-generic
void F2(I1<U> a); // Valid overload
void F2(I2<U> a);
}
class G2<U,V>
{
void F3(U u, V v); // Valid, but overload resolution for
void F3(V v, U u); // G2<int,int>.F3 will fail
void F4(U u, I1<V> v); // Valid, but overload resolution for
void F4(I1<V> v, U u); // G2<I1<int>,int>.F4 will fail
void F5(U u1, I1<V> v2); // Valid overload
void F5(V v1, U u2);
void F6(ref U u); // valid overload
void F6(out V v);
}
Проверка динамического разрешения перегрузки во время компиляции
Для основных динамически связанных операций набор возможных кандидатов для разрешения во время компиляции неизвестен. В некоторых случаях, однако, набор кандидатов известен во время компиляции:
· Вызовы статических методов с динамическими аргументами
· Вызовы методов экземпляра, в случаях, когда получатель не является динамическим выражением
· Вызовы индексатора, в случаях, когда получатель не является динамическим выражением
· Вызовы конструктора с динамическими аргументами
В этих случаях для каждого кандидата выполняется ограниченная проверка времени компиляции, выявляющая потенциальную применимость во время выполнения. Эта проверка состоит из следующих этапов:
· Частичный вывод типа: Любой аргумент типа, не зависящий напрямую или косвенно от аргумента типа dynamic, выводится с использованием правил §7.5.2. Остальные аргументы типа неизвестны.
· Частичная проверка применимости: Проверяется применимость в соответствии с §7.5.3.1, но параметры, тип которых неизвестен, пропускаются.
Если кандидатов, прошедших эту проверку, нет, происходит ошибка времени компиляции.
Вызов функции-члена
В этом разделе описывается процесс, который происходит во время выполнения при вызове определенной функции-члена. Предполагается, что во время компиляции уже была определена конкретная вызываемая функция-член, вероятно, с помощью разрешения перегрузки для набора кандидатов функций-членов.
В целях описания процесса вызова функции-члены разделены на две категории.
· Статические функции-члены. Это конструкторы экземпляров, статические методы, методы доступа к статическим свойствам и пользовательские операторы. Статические функции-члены всегда являются невиртуальными.
· Функции-члены экземпляра. Это методы экземпляра, методы доступа к свойствам и индексаторам. Функции-члены экземпляра могут быть либо виртуальными, либо невиртуальными и всегда вызываются для конкретного экземпляра. Экземпляр вычисляется по выражению экземпляра и оказывается доступен внутри функции-члена с помощью оператора this (§7.6.7).
Обработка вызова функции-члена во время выполнения состоит из следующих этапов, где M —это функция-член, а E — это выражение экземпляра (когда M является членом экземпляра).
· Если M является статической функцией-членом
o Список аргументов вычисляется, как описано в разделе §7.5.1.
o Вызывается M.
· Если M является функцией-членом экземпляра, объявленной в типе значения:
o Вычисляется E. Если при этом вычислении возникает исключение, дальнейшие этапы не выполняются.
o Если E не является переменной, то создается временная локальная переменная типа E и этой переменной присваивается значение E. После этого E становится ссылкой на эту временную локальную переменную. Временная переменная доступна с помощью оператора this в M, но не другим способом. Таким образом, вызывающий может наблюдать изменения, проводимые в M для this, только когда E является настоящей переменной.
o Список аргументов вычисляется, как описано в разделе §7.5.1.
o Вызывается M. Переменная, на которую ссылается E, становится переменной, на которую ссылается this.
· Если M является функцией-членом экземпляра, объявленной в ссылочном типе:
o Вычисляется E. Если при этом вычислении возникает исключение, дальнейшие этапы не выполняются.
o Список аргументов вычисляется, как описано в разделе §7.5.1.
o Если E имеет тип значения, выполняется преобразование упаковки (§4.3.1) для преобразования E в тип object. На следующих этапах считается, что E имеет тип object. В данном случае M может быть только членом System.Object.
o Выполняется проверка допустимости значения E. Если E имеет значение null, вызывается исключение System.NullReferenceException и дальнейшие действия не выполняются.
o Определяется реализация вызываемой функции-члена.
· Если типом E во время компиляции является интерфейс, то вызываемой функцией-членом является реализация M, предоставленная типом времени выполнения экземпляра, на который ссылается E. Эта функция-член определяется применением правил сопоставления интерфейса (§13.4.4), чтобы определить реализацию M, предоставленную типом времени выполнения экземпляра, на который ссылается E.
· Иначе, если M является виртуальной функцией-членом, вызываемая функция-член является реализацией M, предоставленной типом времени выполнения экземпляра, на который ссылается E. Эта функция-член определяется применением правил определения самой производной реализации (§10.6.3) M в соответствии с типом времени выполнения экземпляра, на который ссылается E.
· Иначе M является невиртуальной функцией-членом, и вызываемой функцией-членом является сама M.
o Вызывается реализация функции-члена, определенная на этапе выше. Объект, на который ссылается E, становится объектом, на который ссылается this.