При сравнении делегатов учитывается не только метод, на который ссылается делегат, но и ссылка на объект, с которым связан метод.
MulticastDelegate.Combine и MulticastDelegate.Remove
Методы предназначены для поддержки делегатов, ссылающихся одновременно на несколько функций. Первый метод позволяет объединить несколько делегатов в один, в списке вызовов которого будут находиться списки вызовов всех скомбинированных делегатов.
// Объединяет два делегата в один.public static Delegate Delegate.Combine(// Первый делегат. Delegate, // Второй делегат. Delegate);// Объединяет произвольное количество делегатов в один.public static Delegate Delegate.Combine(// Массив делегатов, которые будут // объединены в один. Delegate[]);
Обратите внимание на то, что в результате работы метода будет возвращен новый делегат, который в своем списке вызовов будет содержать функции всех указанных делегатов. При этом старые делегаты при выполнении данной операции абсолютно не пострадают, их списки вызова останутся нетронутыми.
Наряду с возможностью объединения нескольких делегатов в один, существует обратная операция — изъятия указанных методов из списка вызовов некоторого делегата.
// Позволяет изъять из делегата связь с определенными функциями, // представленные также делегатами.public static Delegate Delegate.Remove(// Делегат-источник. Delegate source, // Функции, связанные с этим делегатом, // будут изъяты из делегата-источника. Delegate value);
Рассмотренный метод, также как и метод Combine, не изменяет значение параметров, переданных ему. Он создает абсолютно новый делегат, в который помещает результаты своей работы.
Разработчики среды.NET решили, что простым программистам будет затруднительно использовать эти методы, в силу их нетривиальности. Поэтому в компилятор C# были введены специальные средства поддержки делегатов. Вы можете оперировать с делегатами при помощи обычных операций сложения и вычитания (+,-,+=,-=). При этом компилятор автоматически будет генерировать код, обращающийся к методам Combine и Remove.
Для наглядности приведем пример, демонстрирующий работу с делегатами, ссылающихся на несколько методов (листинг 6).
Листинг 6. Работа с делегатами, ссылающимися на несколько методов.
using System; class App{ delegate void MyDelegate(string s); // Это функция, на которую мы будем ссылаться при помощи // экземпляра делегата. static void MyHandler(string s) { // Просто выведем на консоль аргумент, полученный нашей // функцией. Console.WriteLine(s); } static void Main() { // Создадим экземпляр делегата, описанного ранее. // В качестве единственного параметра конструктора // передадим ссылку на метод MyHandler. MyDelegate del = new MyDelegate(MyHandler); // Создадим новый делегат и скомбинируем его // с ранее созданным делегатом. del += new MyDelegate(MyHandler); // То же самое можно сделать следующим образом. del = del + new MyDelegate(MyHandler); // А теперь уберем из списка вызова делегата // одну из ссылок на метод MyHanlder. del -= new MyDelegate(MyHanlder); // Затем обратимся к конечному делегату, естественно, // что при этом по цепочке будут вызваны все методы, // связанные с ним. del("Hello, world!"); }};
В результате работы приложения на консоль будут выведены следующие строки.
Hello, World!Hello, World!
Обратите внимание, что функция была вызвана два раза, хотя мы добавляли ее в список вызова делегата три раза. Но если учесть, что мы один раз изъяли ее из списка вызова, то все становится понятно. Интересно, что в списке вызова делегата все методы одинаковы, но этот факт для сервисов, оперирующих с делегатами, не имеет никакого значения.
Компилятор преобразует арифметические операции над делегатами в обращения к методам Combine и Remove. Чтобы наглядно продемонстрировать это, приведу листинг функции Main на языке IL, который получился после транслирования исходного кода компилятором языка C# (листинг 7).
Листинг 7. Код функции Main на языке IL.
.method private hidebysig static void Main() cil managed{.entrypoint.maxstack 4.locals init (class App/MyDelegate V_0) // Загружаем нулевой указатель — функция статическая, // не привязанная к экземпляру объекта. ldnull // Загружаем указатель на функцию. ldftn void App::MyHandler(string) // Создаем экземпляр делегата. newobj instance void App/MyDelegate::.ctor(object, native int) stloc.0 ldloc.0 // Создаем еще один точно такой же делегат с одним // методом в списке вызова. ldnull ldftn void App::MyHandler(string) newobj instance void App/MyDelegate::.ctor(object, native int) // Здесь как раз и происходит комбинирование делегатов, // на уровне языка С# это выглядело как оператор сложения (+=). call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass App/MyDelegate stloc.0 ldloc.0 // Создаем еще один точно такой же делегат с одним // методом в списке вызова. ldnull ldftn void App::MyHandler(string) newobj instance void App/MyDelegate::.ctor(object, native int) // Здесь как раз и происходит комбинирование делегатов, // на уровне языка С# это выглядело как оператор сложения (+). call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass App/MyDelegate stloc.0 ldloc.0 // Создаем еще один точно такой же делегат с одним // методом в списке вызова. ldnull ldftn void App::MyHandler(string) newobj instance void App/MyDelegate::.ctor(object, native int) // А здесь происходит изъятие метода из списка вызова делегата, // на уровне языка С# это выглядело как оператор вычитания (-=). call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass App/MyDelegate stloc.0 ldloc.0 ldstr "Hello, world!" // А здесь происходит обращение к делегату. // Более подробно этот момент рассмотрим несколько позднее, // хотя, взглянув на следующую строку, вы можете обо всем // догадаться самостоятельно. callvirt instance void App/MyDelegate::Invoke(string) ret}
Понимая, что чтение IL-листингов может быть для некоторых читателей несколько утомительно, приведем код примера на языке C#, напрямую использующего методы Combine и Remove (листинг 8).
Листинг 8. Пример, явно использующий методы Combine и Remove.
using System; class App{ delegate void MyDelegate(string s); // Это функция, на которую мы будем ссылаться при помощи // экземпляра делегата. static void MyHandler(string s) { // Просто выведем на консоль аргумент, полученный нашей // функцией. Console.WriteLine(s); } // Точка входа в приложение. static void Main() { // Создадим экземпляр делегата, описанного выше. // В качестве единственного параметра конструктора // передадим ссылку на метод MyHandler. MyDelegate del = new MyDelegate(MyHandler); // Создадим новый делегат и скомбинируем его // с ранее созданным делегатом. //del += new MyDelegate(MyHandler); del = (MyDelegate)Delegate.Combine(del,new MyDelegate(MyHandler)); // То же самое можно сделать следующим образом. // del = del + new MyDelegate(MyHandler); del = (MyDelegate)Delegate.Combine(del,new MyDelegate(MyHandler)); // А теперь уберем из списка вызова делегата // одну из ссылок на метод MyHanlder. //del -= new MyDelegate(MyHanlder); del = (MyDelegate)Delegate.Remove(del,new MyDelegate(MyHandler)); // А так мы вызовем функцию через делегат. // Как видите, все просто, мы можем пользоваться // экземпляром делегата как функцией. del("Hello, world!"); }};
Результат работы данного примера нисколько не будет отличаться от предыдущего.
Hello, World!Hello, World!
Таким образом, использование методов Combine, Remove и арифметических операций, предоставляемых компилятором, по сути дела, ничем не отличается. Разве что обращение ко вторым более удобно, и программы, написанные с их использованием, читаются гораздо легче.
Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет
studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав!Последнее добавление