Тема 3.4. Структуры. Делегаты.

План:


Делегаты в C# позволяют применять методы одного класса к объектам другого класса, который может эти методы вызвать. Вы можете применить метод m класса A, определенного в делегате, к классу B, который будет способен вызвать метод m класса A. Вы можете вызвать как статические методы, так и нестатические. Эта концепция хорошо известна разработчикам C++, которые использовали указатель на функцию в качестве параметра, передаваемого другой функции. Концепция делегатов была введена в Visual J++, а затем перенесена в C#. Делегаты C# являются в .NET Framework реализацией в качестве производного класса от System.Delegate. Использование делегатов можно описать четырьмя шагами.

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

Определение метода с сигнатурой делегата, определенного на первом шаге

Создание объекта делегата и связывание их с методами

Вызов связанного метода с помощью объекта делегата

Слово делегат (delegate) используется в C# для обозначения хорошо известного понятия. Делегат задает определение функционального типа (класса) данных. Экземплярами класса являются функции. Описание делегата в языке C# представляет собой описание еще одного частного случая класса. Каждый делегат описывает множество функций с заданной сигнатурой. Каждая функция (метод), сигнатура которого совпадает с сигнатурой делегата, может рассматриваться как экземпляр класса, заданного делегатом. Синтаксис объявления делегата имеет следующий вид:

[<спецификатор доступа>] delegate <тип результата > <имя класса> (<список аргументов>);

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

Спецификатор доступа может быть, как обычно, опущен. Где следует размещать объявление делегата? Как и у всякого класса, есть две возможности:

• непосредственно в пространстве имен, наряду с объявлениями других классов, структур, интерфейсов;

• внутри другого класса, наряду с объявлениями методов и свойств. Такое объявление рассматривается как объявление вложенного класса.

Так же, как и интерфейсы C#, делегаты не задают реализации. Фактически между некоторыми классами и делегатом заключается контракт на реализацию делегата. Классы, согласные с контрактом, должны объявить у себя статические или динамические функции, сигнатура которых совпадает с сигнатурой делегата. Если контракт выполняется, то можно создать экземпляры делегата, присвоив им в качестве значений функции, удовлетворяющие контракту. Заметьте, контракт является жестким: не допускается ситуация, при которой у делегата тип параметра - object, а у экземпляра соответствующий параметр имеет тип, согласованный сobject, например,int.

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

namespace Delegates

{
//объявление классов - делегатов
delegate void Proc(ref int x);
delegate void MesToPers(string s);
class OwnDel
{
public delegate int Fun1(int x);
int Plus1( int x){return(x+100);}//Plus1
int Minus1(int x){return(x-100);}//Minus1
void Plus(ref int x){x+= 100;}
void Minus(ref int x){x-=100;}
//поля класса
public Proc p1;
public Fun1 f1;
char sign;
//конструктор
public OwnDel(char sign)
{
this.sign = sign;
if (sign == '+')
{p1 = new Proc(Plus);f1 = new Fun1(Plus1);}
else
{p1 = new Proc(Minus);f1 = new Fun1(Minus1);}
}
}//class OwnDel

• Первым делом объявлены три функциональных класса - три делегата: Proc, MesToPers, Fun1. Каждый из них описывает множество функций фиксированной сигнатуры.

• В классе OwnDel описаны четыре метода: Plus, Minus, Plus1, Minus1, сигнатуры которых соответствуют сигнатурам, задаваемых классами Proc и Fun1.

• Поля p1 и f1 класса OwnDel являются экземплярами классов Proc и Fun1.

• В конструкторе класса поля p1 и f1 связываются с конкретными методами Plus или Minus, Plus1 или Minus1. Связывание с той или иной функцией в данном случае определяется значением поля sign.

Заметьте, экземпляры делегатов можно рассматривать как ссылки (указатели на функции), а методы тех или иных классов с соответствующей сигнатурой можно рассматривать как объекты, хранимые в динамической памяти. В определенный момент происходит связывание ссылки и объекта (в этой роли выступают не обычные объекты, имеющие поля, а методы, задающие код). Взгляд на делегата как на указатель функции характерен для программистов, привыкших к С++.

Приведу теперь процедуру, тестирующую работу созданного класса:

public void TestOwnDel()
{
int account = 1000, account1=0;
OwnDel oda = new OwnDel('+');
Console.WriteLine("account = {0}, account1 = {1}",
account, account1);
oda.p1(ref account); account1=oda.f1(account);
Console.WriteLine("account = {0}, account1 = {1}",
account, account1);
}

Клиент класса OwnDel создает экземпляр класса, передавая конструктору знак той операции, которую он хотел бы выполнить над своими счетами -account иaccount1. Вызовp1 иf1, связанных к моменту вызова с закрытыми методами класса, приводит к выполнению нужных функций.

Еще пример. Следующий C# код показывает вышеупомянутые шаги в реализации одного делегата и четырех классов.

using System;
// Шаг 1. Определение делегата с сигнатурой связываемого метода
public delegate void MyDelegate(string input);
// Шаг 2. Определение метода с сигнатурой определенного делегата
class MyClass1{
public void delegateMethod1(string input){
Console.WriteLine(
"This is delegateMethod1 and the input to the method is {0}",input);
}
public void delegateMethod2(string input){
Console.WriteLine("This is delegateMethod2 and the input to the method is {0}",input);
}
}
// Шаг 3. Создание объектов делегата и связывание с методами
class MyClass2{
public MyDelegate createDelegate(){
MyClass1 c2=new MyClass1();
MyDelegate d1 = new MyDelegate(c2.delegateMethod1);
MyDelegate d2 = new MyDelegate(c2.delegateMethod2);
MyDelegate d3 = d1 + d2;
return d3;
}
}
// Шаг 4. Вызов метода с помощью делегата
class MyClass3{
public void callDelegate(MyDelegate d,string input){
d(input);
}
}
class Driver{
static void Main(string[] args){
MyClass2 c2 = new MyClass2();
MyDelegate d = c2.createDelegate();
MyClass3 c3 = new MyClass3();
c3.callDelegate(d,"Calling the delegate");

}
}
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
namespaceDelegates
{
   // Объявление делегата:
   delegate string StrMod(string str);
   class DelegateTest
   {
       // Заменить пробелы дефисами:
       static string ReplaceSpaces(string s)
       {
           Console.WriteLine("Замена пробелов дефисами: ");
           return s.Replace(' ', '-');
       }
       // Удалить пробелы
       static string RemoveSpaces(string s)
       {
           string temp = "";
           int i;
           Console.WriteLine("Удаление пробелов:");
           for (i = 0; i < s.Length; i++)
               if (s[i] != ' ') temp += s[i];
           return temp;
       }
       // Обратить строку
       static string Reverse(string s)
       {
           string temp = "";
           int i, j;
           Console.WriteLine("Обращение строки:");
           for (j = 0, i = s.Length - 1; i >= 0; i--, j++)
               temp += s[i];
           return temp;
       }
       static void Main(string[] args)
       {
           // Сконструировать делегат
           StrMod delObj;
           string str;
           // Вызов методов с помощью делегата
           delObj = ReplaceSpaces;
           str = delObj("Это тестовое сообщение");
           Console.WriteLine("Результирующая строка: {0}", str);
           Console.WriteLine();
           delObj = RemoveSpaces;
           str = delObj("Это тестовое сообщение");
           Console.WriteLine("Результирующая строка: {0}", str);
           Console.WriteLine();
           delObj = Reverse;
           str = delObj("Это тестовое сообщение");
           Console.WriteLine("Результирующая строка: {0}", str);
           Console.ReadKey();
       }
   }
}
// Результаты работы программы:
// Замена пробелов дефисами:
// Результирующая строка: Это-тестовое-сообщение
// Удаление пробелов:
// Результирующая строка: Этотестовоесообщение
// Обращение строки:
// Результирующая строка: еинещбоос еовотсет отЭ

This site was made on Tilda — a website builder that helps to create a website without any code
Create a website