Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharp Language Specification.doc
Скачиваний:
12
Добавлен:
26.09.2019
Размер:
4.75 Mб
Скачать

15.2Совместимость делегатов

Метод или делегат M совместим с типом делегата D, если верны все следующие условия:

  • D и M имеют одинаковое число параметров, и каждый параметр в D имеет такие же модификаторы ref или out, как и соответствующий параметр в M;

  • для каждого параметра значения (параметр без модификатора ref или out) существует преобразование идентификации (§6.1.1) или неявное преобразование ссылки (§6.1.6) из типа параметра в D в соответствующий тип параметра в M;

  • для каждого параметра ref или out тип параметра в D такой же, как тип параметра в M;

  • существует преобразование идентификации или неявное преобразование ссылки из типа возвращаемого значения M в тип возвращаемого значения D.

15.3Создание экземпляра делегата

Экземпляр делегата создается выражением_создания_делегата (§7.6.10.5) или преобразованием в тип делегата. Вновь созданный экземпляр делегата затем ссылается на одно из следующего:

  • на статический метод, на который имеется ссылка в выражении_создания_делегата, или

  • на целевой объект (который не может иметь значение null) и метод экземпляра, на которые имеются ссылки в выражении_создания_делегата, или

  • на другой делегат.

Пример:

delegate void D(int x);

class C { public static void M1(int i) {...} public void M2(int i) {...} }

class Test { static void Main() { D cd1 = new D(C.M1); // static method C t = new C(); D cd2 = new D(t.M2); // instance method D cd3 = new D(cd2); // another delegate } }

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

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

Язык C# предоставляет специальный синтаксис для вызова делегата. Когда вызывается непустой экземпляр делегата, чей список вызова содержит одну запись, он вызывает один метод с теми же аргументами, которые были ему заданы, и возвращает то же самое значение, что метод, на который имеется ссылка (подробные сведения о вызове делегата см. в §7.6.5.3). Если при вызове такого делегата происходит исключение, и это исключение не перехватывается в вызванном методе, поиск предложения перехвата исключения продолжается в методе, вызвавшем делегат, как если бы этот метод напрямую вызвал метод, на который ссылался делегат.

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

Если при обработке вызова такого делегата происходит исключение, и это исключение не перехватывается в вызванном методе, поиск предложения перехвата исключения продолжается в методе, вызвавшем делегат, а все методы, указанные далее в списке вызова, не вызываются.

Попытка вызова экземпляра делегата, имеющего значение null, приводит к исключению типа System.NullReferenceException.

В следующем примере показано, как создавать экземпляры, объединять, удалять и вызывать делегаты:

using System;

delegate void D(int x);

class C { public static void M1(int i) { Console.WriteLine("C.M1: " + i); }

public static void M2(int i) { Console.WriteLine("C.M2: " + i); }

public void M3(int i) { Console.WriteLine("C.M3: " + i); } }

class Test { static void Main() { D cd1 = new D(C.M1); cd1(-1); // call M1

D cd2 = new D(C.M2); cd2(-2); // call M2

D cd3 = cd1 + cd2; cd3(10); // call M1 then M2

cd3 += cd1; cd3(20); // call M1, M2, then M1

C c = new C(); D cd4 = new D(c.M3); cd3 += cd4; cd3(30); // call M1, M2, M1, then M3

cd3 -= cd1; // remove last M1 cd3(40); // call M1, M2, then M3

cd3 -= cd4; cd3(50); // call M1 then M2

cd3 -= cd2; cd3(60); // call M1

cd3 -= cd2; // impossible removal is benign cd3(60); // call M1

cd3 -= cd1; // invocation list is empty so cd3 is null

// cd3(70); // System.NullReferenceException thrown

cd3 -= cd1; // impossible removal is benign } }

Как показано в выражении cd3 += cd1;, делегат может присутствовать в списке вызова несколько раз. В этом случае он просто вызывается один раз при каждом появлении. В таком списке вызова, как этот, при удалении делегата фактически удаляется его последнее появление в списке вызова.

Непосредственно перед выполнением последнего оператора, cd3 -= cd1;, делегат cd3 ссылается на пустой список вызова. Попытка удаления делегата из пустого списка (или удаления несуществующего делегата из непустого списка) не является ошибкой.

В результате получается:

C.M1: -1 C.M2: -2 C.M1: 10 C.M2: 10 C.M1: 20 C.M2: 20 C.M1: 20 C.M1: 30 C.M2: 30 C.M1: 30 C.M3: 30 C.M1: 40 C.M2: 40 C.M3: 40 C.M1: 50 C.M2: 50 C.M1: 60 C.M1: 60

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]