Делегирование (англ.Delegation) — основнойшаблон проектирования, в котором объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту. Шаблон делегирования является фундаментальной абстракцией, на основе которой реализованы другие шаблоны - композиция (также называемая агрегацией), примеси (mixins) и аспекты (aspects).
В этом примере на языке Java, класс B имеет метод-заглушку (method stub), который передаёт методыfoo() и bar() классу A. Класс B делает вид, что он имеет атрибуты класса A.
Исходный текст на языке java
classA{voidfoo(){System.out.println("A: вызван метод foo()");}voidbar(){System.out.println("A: вызван метод bar()");}}classB{// Создаём объект, методам которого будет делегироваться поведение.Aa=newA();voidfoo(){a.foo();}voidbar(){a.bar();}}publicclassMain{publicstaticvoidmain(String[]args){Bb=newB();b.foo();b.bar();}}
Сложный пример
Используя интерфейсы, делегирование можно осуществить более гибко и c защитой типов (typesafe). В этом примере, класс C может делегировать либо классу A либо классу B. Класс C имеет методы для переключения между классами A и B. Включение расширения implements улучшает безопасность типа, потому что каждый класс должен выполнять методы в интерфейсе. Основным недостатком является большее количество кода.
Приведем пример. Допустим, нужно реализовать таймер таким образом, чтобы через определённое количество времени вызывалась некоторая функция. Программист таймера хочет предоставить задание функции пользователям своего класса (другим программистам).
Исходный текст на языке java
/** * Интерфейс описывает действие, которое будет вызываться при наступлении * события от таймера. */interfaceTimerAction{voidonTime();}classWakeUpActionimplementsTimerAction{@OverridepublicvoidonTime(){System.out.println("Пора вставать!");}}classChickenIsReadyActionimplementsTimerAction{@OverridepublicvoidonTime(){System.out.println("Цыплёнок готов!");}}/** * Класс таймера. При определённых условиях вызывается действие TimerAction. */classTimer{TimerActionaction;/** * Функция, которую вызывает программист для установки времени. */voidrun(){if(isTime()){action.onTime();}}/** * Некоторая функция, которая берет на себя всю работу со временем. Её * реализация не интересна в данном контексте. * * @return */privatebooleanisTime(){returntrue;}publicstaticvoidmain(String[]args){System.out.println("Введите тип действия:");Scannerscanner=newScanner(System.in);StringactionType=scanner.nextLine();Timertimer=newTimer();if(actionType.equalsIgnoreCase("set wake up timer")){timer.action=newWakeUpAction();}elseif(actionType.equalsIgnoreCase("set chicken timer")){timer.action=newChickenIsReadyAction();}timer.run();}
Этот пример — это версия на C++ сложного примера на Java, приведённого выше. Так как C++ не имеет конструкции интерфейса, ту же самую роль играет полностью абстрактный класс. Преимущества и недостатки в основном те же самые, что и в примере на Java.
Исходный текст на языке c++
#include<iostream>classI{public:virtualvoidf()=0;virtualvoidg()=0;};classA:publicI{public:voidf(){std::cout<<"A: вызываем метод f()"<<std::endl;}voidg(){std::cout<<"A: вызываем метод g()"<<std::endl;}};classB:publicI{public:voidf(){std::cout<<"B: вызываем метод f()"<<std::endl;}voidg(){std::cout<<"B: вызываем метод g()"<<std::endl;}};classC:publicI{public:// КонструкторC():m_i(newA()){}// Деструкторvirtual~C(){deletem_i;}voidf(){m_i->f();}voidg(){m_i->g();}// Этими методами меняем поле-объект, чьи методы будем делегироватьvoidtoA(){deletem_i;m_i=newA();}voidtoB(){deletem_i;m_i=newB();}private:// Объявляем объект методы которого будем делегироватьI*m_i;};intmain(){Cc;c.f();c.g();c.toB();c.f();c.g();return0;}/* Output:A: вызываем метод f()A: вызываем метод g()B: вызываем метод f()B: вызываем метод g()*/
namespacePatterns{interfaceI{voidf();voidg();}classA:I{publicvoidf(){System.Console.WriteLine("A: вызываем метод f()");}publicvoidg(){System.Console.WriteLine("A: вызываем метод g()");}}classB:I{publicvoidf(){System.Console.WriteLine("B: вызываем метод f()");}publicvoidg(){System.Console.WriteLine("B: вызываем метод g()");}}classC:I{// Создаём объект, методы которого будем делегироватьIi=newA();publicvoidf(){i.f();}publicvoidg(){i.g();}// Этими методами меняем поле-объект, чьи методы будем делегироватьpublicvoidtoA(){i=newA();}publicvoidtoB(){i=newB();}}classDelegatePattern{staticvoidMain(string[]args){Cc=newC();c.f();c.g();c.toB();c.f();c.g();System.Console.ReadKey();}}}
Нетривиальный пример
Это пример случая, часто встречающегося в практике. Стоит задача создать класс для хранения списка сотрудников. Данные каждого сотрудника хранятся в объекте класса Employee. Есть уже готовый и стандартный класс для хранения списка объектов Employee. В нём уже реализованы механизмы для работы со списком (к примеру — выделение памяти, добавление и удаление из списка). Наследование класса списка сотрудников от класса списка объектов здесь неприемлемо, потому как мы получим все методы (даже те, которые нас не интересуют). Кроме того нам придётся в некоторых случаях производить приведение типов. Самый элегантный выход из этого случая — делегировать классу списка сотрудников часть методов класса списка объектов. В правилах ООП лучше всего список объектов представить частным (приватным) методом списка сотрудников. В данном случае доступ к списку возможен через индексатор.
Исходный текст на языке C#
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;namespaceEmployees{/// <summary>/// Класс для хранения данных о сотруднике./// </summary>classEmployee{privatestringname;privatestringdepartment;publicEmployee(stringname,stringdepartament){this.name=name;this.department=departament;}/// <summary>/// Имя сотрудника./// </summary>publicstringName{get{returnthis.name;}}/// <summary>/// Отдел работы./// </summary>publicstringDepartment{get{returnthis.department;}}}/// <summary>/// Класс для хранения списка сотрудников./// </summary>classEmployeesList{privateList<Employee>employees=newList<Employee>();/// <summary>/// Свойство для получения и записи сотрудника по индексу./// </summary>/// <param name="index">Индекс сотрудника.</param>/// <returns>Сотрудник.</returns>publicEmployeethis[intindex]{get{returnemployees[index];}set{employees[index]=value;}}/// <summary>/// Добавление нового сотрудника./// </summary>/// <param name="employee">Новый сотрудник.</param>publicvoidAdd(Employeeemployee){employees.Add(employee);}/// <summary>/// Удаление существующего сотрудника./// </summary>/// <param name="employee">Сотрудник для удаления.</param>publicvoidRemove(Employeeemployee){employees.Remove(employee);}/// <summary>/// Последовательный поиск сотрудника по имени./// </summary>/// <param name="name">Имя сотрудника.</param>/// <param name="offset">Позиция, с которой следует начинать поиск.</param>/// <returns>Индекс сотрудника.</returns>publicintGetIndexOfEmployeeByName(stringname,intoffset=0){for(inti=offset;i<employees.Count;i++){if(employees[i].Name==name){returni;}}return-1;}}classProgram{staticvoidMain(string[]args){//Создание списка сотрудников и добавление записей в негоEmployeesListempList=newEmployeesList();empList.Add(newEmployee("Шлёнский Дмитрий","web студия"));empList.Add(newEmployee("Кусый Назар","web студия"));empList.Add(newEmployee("Сорока Орест","web студия"));//Поиск сотрудника Кусый Назар и вывод результата при поиске с начала и со 2-й позицииConsole.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар").ToString());Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар",2).ToString());//Поиск и удаление сотрудника Сорока ОрестempList.Remove(empList[empList.GetIndexOfEmployeeByName("Сорока Орест")]);}}}
Исходный текст на языке C# 2
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;namespaceEmployees{/// <summary>/// Класс для хранения данных о сотруднике./// </summary>classEmployee{privatestringname;privatestringdepartment;publicEmployee(stringname,stringdepartament){this.name=name;this.department=departament;}/// <summary>/// Имя сотрудника./// </summary>publicstringName{get{returnthis.name;}}/// <summary>/// Отдел работы./// </summary>publicstringDepartment{get{returnthis.department;}}}/// <summary>/// Класс для хранения списка сотрудников./// </summary>classEmployeesList{privateList<Employee>employees=newList<Employee>();/// <summary>/// Свойство для получения и записи сотрудника по индексу./// </summary>/// <param name="index">Индекс сотрудника.</param>/// <returns>Сотрудник.</returns>publicEmployeethis[intindex]{get{returnemployees[index];}set{employees[index]=value;}}/// <summary>/// Свойство для получения и записи сотрудника по имени./// </summary>/// <param name="name">Имя сотрудника.</param>/// <returns>Первого сотрудник, у которого совпало имя или null</returns>publicEmployeethis[stringname]{get{foreach(Employeeiteminemployees){if(item.Name==name)returnitem;}returnnull;}}/// <summary>/// Добавление нового сотрудника./// </summary>/// <param name="employee">Новый сотрудник.</param>publicvoidAdd(Employeeemployee){employees.Add(employee);}/// <summary>/// Удаление существующего сотрудника./// </summary>/// <param name="employee">Сотрудник для удаления.</param>publicvoidRemove(Employeeemployee){employees.Remove(employee);}/// <summary>/// Последовательный поиск сотрудника по имени./// </summary>/// <param name="name">Имя сотрудника.</param>/// <param name="offset">Позиция, с которой следует начинать поиск.</param>/// <returns>Индекс сотрудника.</returns>publicintGetIndexOfEmployeeByName(stringname,intoffset){intindex=-1;for(inti=offset;i<employees.Count;i++){if(employees[i].Name==name){index=i;break;}}returnindex;}/// <summary>/// Последовательный поиск сотрудника по имени./// </summary>/// <param name="name">Имя сотрудника.</param>/// <returns>Индекс сотрудника.</returns>publicintGetIndexOfEmployeeByName(stringname){returnGetIndexOfEmployeeByName(name,0);}}classProgram{staticvoidMain(string[]args){//Создание списка сотрудников и добавление записей в негоEmployeesListempList=newEmployeesList();empList.Add(newEmployee("Шлёнский Дмитрий","web студия"));empList.Add(newEmployee("Кусый Назар","web студия"));empList.Add(newEmployee("Сорока Орест","web студия"));//Поиск сотрудника Кусый Назар и вывод результата при поиске с начала и со 2-й позицииConsole.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар").ToString());Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар",2).ToString());//Поиск и удаление сотрудника Сорока ОрестempList.Remove(empList["Сорока Орест"]);}}}
@interfaceGLEngine(Internal)-(void)registerDelegate;@end@implementationGLEngine-(id)delegate{return_delegate;}-(void)setDelegate:(id)delegate{_delegate=delegate;[selfregisterDelegate];}-(void)changeView:(GLView*)view{if(_event[GLEngineChangeView]){// делегируем запросif([_delegateengine:selfchangeView:view]){// используем изменённый вид}else{// запрос не был обработан }}// или используем стандартный способ}-(void)registerDelegate{if([_delegateresponseToSelector:@selector(engine:changeView:)]){_event[GLEngineChangeView]=YES;}}@end
Этот пример — это версия на Object Pascal нетривиального примера, приведённого выше.
Исходный текст на языке Object Pascal
unitUnitEmployeers;interfaceusesContnrs;type// Класс для хранения данных о сотрудникеTEmployee=classprivateFName:string;FDepartament:string;publicconstructorCreate(Name,Departament:string);publishedpropertyName:stringreadFName;propertyDepartament:stringreadFDepartament;end;// Класс для хранения списка сотрудниковTEmployeersList=classprivate// Объект класса "список объектов"FEmployeersList:TObjectList;functionGetEmployee(Index:Integer):TEmployee;procedureSetEmployee(Index:Integer;constValue:TEmployee);publicconstructorCreate;destructorDestroy;override;functionAdd(Employee:TEmployee):Integer;procedureRemove(Employee:TEmployee);functionIndexEmployeeByName(Name:string;Offset:Integer=0):Integer;propertyEmployeers[Index:Integer]:TEmployeereadGetEmployeewriteSetEmployee;default;end;implementation{ TEmployee }constructorTEmployee.Create(Name,Departament:string);beginFName:=Name;FDepartament:=Departament;end;{ TEmployeersList }constructorTEmployeersList.Create;begin// Создаём объект методы которого будем делегироватьFEmployeersList:=TObjectList.Create;end;destructorTEmployeersList.Destroy;beginFEmployeersList.Free;inherited;end;functionTEmployeersList.GetEmployee(Index:Integer):TEmployee;beginResult:=FEmployeersList[Index]asTEmployee;end;procedureTEmployeersList.SetEmployee(Index:Integer;constValue:TEmployee);beginFEmployeersList[Index]:=Value;end;functionTEmployeersList.IndexEmployeeByName(Name:string;Offset:Integer=0):Integer;// Последовательный поиск сотрудника по имени// Через аргумент Offset можно задавать позицию с которой вести поиск.// Если сотрудник не найден вернёт значение меньше ноля (-1)varIndex:Integer;beginResult:=-1;// Предполагаем что его нет в спискеforIndex:=FEmployeersList.Count-1downtoOffsetdoif(FEmployeersList[Index]asTEmployee).Name=NamethenbeginResult:=Index;Exit;end;end;functionTEmployeersList.Add(Employee:TEmployee):Integer;beginResult:=FEmployeersList.Add(Employee);end;procedureTEmployeersList.Remove(Employee:TEmployee);beginFEmployeersList.Remove(Employee);end;end.
К сожалению, не все программисты применяют шаблон делегирования. Например, фирма Borland (разработчик среды программирования Delphi) в своей стандартной библиотеке классов наследовала вышеупомянутый класс списка объектов TObjectList от класса списка указателей TList. Это вызвало недовольство среди некоторых опытных программистов.
Этот пример — это версия на PHP простого примера на Java, приведённого выше.
Исходный текст на языке PHP5
<?phpclassA{publicfunctionf(){print"А: Вызываем метод f()<br />";}publicfunctiong(){print"А: Вызываем метод g()<br />";}}classC{private$_a;publicfunction__construct(){$this->_a=newA;}publicfunctionf(){$this->_a->f();}publicfunctiong(){$this->_a->g();}publicfunctiony(){print"C: вызываем метод y()<br />";}}$obj=newC;$obj->f();$obj->g();$obj->y();?>
Сложный пример
Этот пример — это версия на PHP сложного примера на Java, приведённого выше.
Исходный текст на языке PHP5
<?php// используем интерфейс для безопасности типаinterfaceI{publicfunctionf();publicfunctiong();}classAimplementsI{publicfunctionf(){print"А: Вызываем метод f()<br />";}publicfunctiong(){print"А: Вызываем метод g()<br />";}}classBimplementsI{publicfunctionf(){print"B: Вызываем метод f()<br />";}publicfunctiong(){print"B: Вызываем метод g()<br />";}}classCimplementsI{private$_i;// создаём объект, методы которого будем делегироватьpublicfunction__construct(){$this->_i=newA;}// этими методами меняем поле-объект, чьи методы будем делегироватьpublicfunctiontoA(){$this->_i=newA;}publicfunctiontoB(){$this->_i=newB;}// делегированые методыpublicfunctionf(){$this->_i->f();}publicfunctiong(){$this->_i->g();}}$obj=newC;$obj->f();$obj->g();$obj->toB();$obj->f();$obj->g();?>
Нетривиальный пример
Этот пример — это версия на PHP нетривиального примера, приведённого выше.
Исходный текст на языке PHP5
<?php// класс для хранения данных о сотрудникеclassEmployee{private$_name;private$_departament;publicfunction__construct($name,$departament){$this->_name=$name;$this->_departament=$departament;}publicfunctiongetName(){return$this->_name;}publicfunctiongetDepartament(){return$this->_departament;}}// класс для хранения списка объектовclassObjectList{private$_objList;publicfunction__construct(){$this->free();}/** *чтобы не скучать! */publicfunctionfree(){$this->_objList=array();}publicfunctioncount(){returncount($this->_objList);}publicfunctionadd($obj){array_push($this->_objList,$obj);}publicfunctionremove($obj){$k=array_search($obj,$this->_objList,true);if($k!==false){unset($this->_objList[$k]);}}publicfunctionget($index){return$this->_objList[$index];}publicfunctionset($index,$obj){$this->_objList[$index]=$obj;}}// класс для хранения сотрудниковclassEmployeeList{// объект класса "список объектов"private$_employeersList;publicfunction__construct(){// создаём объект методы которого будем делегировать$this->_employeersList=newObjectList;}publicfunctiongetEmployer($index){return$this->_employeersList->get($index);}publicfunctionsetEmployer($index,Employee$objEmployer){$this->_employeersList->set($index,$objEmployer);}publicfunction__destruct(){$this->_employeersList->free();}publicfunctionadd(Employee$objEmployer){$this->_employeersList->add($objEmployer);}publicfunctionremove(Employee$objEmployer){$this->_employeersList->remove($objEmployer);}// последовательный поиск сотрудника по имени// через аргумент $offset можно задавать позицию с которой вести поиск.// если сотрудник не найден вернёт значение меньше ноля (-1)publicfunctiongetIndexByName($name,$offset=0){$result=-1;// предполагаем, что его нету в списке$cnt=$this->_employeersList->count();for($i=$offset;$i<$cnt;$i++){if(!strcmp($name,$this->_employeersList->get($i)->getName())){$result=$i;break;}}return$result;}}$obj1=newEmployee("Танасийчук Степан","web студия");$obj2=newEmployee("Кусый Назар","web студия");$obj3=newEmployee("Сорока Орест","web студия");$objList=newEmployeeList();$objList->add($obj1);$objList->add($obj2);$objList->add($obj3);echo"<pre>";print_r($objList);echo"<hr>";$index=$objList->getIndexByName("Кусый Назар");$obj4=$objList->getEmployer($index);print_r($obj4);echo"<hr>";$objList->setEmployer(2,$obj4);print_r($objList);echo"</pre>";?>
#coding: utf-8#python 3classA:deff(self):print('A : вызываем метод f')defg(self):print('A : вызываем метод g')classC:def__init__(self):self._A=Adeff(self):returnself._A.f(self)defg(self):returnself._A.g(self)c=C()c.f()#A: вызываем метод fc.g()#A: вызываем метод g
functionA(){this.f=function(){alert("A: вызываем метод f()");};this.g=function(){alert("A: вызываем метод g()");};}functionC(){vara=newA();this.f=function(){a.f();};this.g=function(){a.g();};}varc=newC();c.f();// "A: вызываем метод f()"c.g();// "A: вызываем метод g()"
Сложный пример
Исходный текст на языке JavaScript
functionA(){this.f=function(){alert("A: вызываем метод f()");};this.g=function(){alert("A: вызываем метод g()");};}functionB(){this.f=function(){alert("B: вызываем метод f()");};this.g=function(){alert("B: вызываем метод g()");};}functionC(){// единожды инстанцируем A и Bvara=newA();varb=newB();varcur=a;// ссылка на текущий объект с реализацией методов; по умолчанию - Athis.toA=function(){cur=a;};this.toB=function(){cur=b;};this.f=function(){cur.f();};this.g=function(){cur.g();};}varc=newC();c.f();// "A: вызываем метод f()"c.g();// "A: вызываем метод g()"c.toB();c.f();// "B: вызываем метод f()"c.g();// "B: вызываем метод g()"
Нетривиальный пример
Исходный текст на языке JavaScript
functionEmployee(name,departament){this.getName=function(){returnname;};this.getDepartament=function(){returndepartament;};this.toString=function(){// преобразование в строку для удобного дебагаreturn"Сотрудник "+name+", "+departament;};}functionEmployeesList(){varemployees=[];this.add=function(){// функция принимает произвольное кол-во аргументовfor(vari=0,l=arguments.length;i<l;i++){if(arguments[i].constructor==Employee){employees.push(arguments[i]);}}};this.set=function(obj,index){// проверка типаif(obj.constructor==Employee){deleteemployees[index];employees[index]=obj;}};this.remove=function(obj){for(vari=0,l=employees.length;i<l;i++){if(employees[i]==obj){employees.splice(i,1);i--;l--;}}};this.getByIndex=function(num){returnemployees[num];};this.getIndexByName=function(name,offset){// последовательный поиск сотрудника по имени// через аргумент offset можно задавать позицию с которой вести поиск. (по умолчанию 0)// если сотрудник не найден, вернёт -1for(vari=offset||0,l=employees.length;i<l;i++){if(employees[i].getName()==name)returni;}return-1;};this.toString=function(){// преобразование в строку для удобного дебагаvarret="";for(vari=0,l=employees.length;i<l;i++){ret+=i+": "+employees[i]+"\n";}returnret;};}varo1=newEmployee("Танасийчук Степан","web студия");varo2=newEmployee("Кусый Назар","web студия");varo3=newEmployee("Сорока Орест","web студия");varemps=newEmployeesList();emps.add(o1,o2,o3);// можно добавлять и поодиночкеalert(emps);// "0: Сотрудник Танасийчук Степан, web студия// 1: Сотрудник Кусый Назар, web студия// 2: Сотрудник Сорока Орест, web студия"varobj4=emps.getByIndex(emps.getIndexByName("Кусый Назар"));// получаем ссылку на сотрудникаalert(obj4);// "Сотрудник Кусый Назар, web студия"emps.set(obj4,2);// вместо 2го (от ноля) сотрудника вставляем obj4alert(emps);// "0: Сотрудник Танасийчук Степан, web студия// 1: Сотрудник Кусый Назар, web студия// 2: Сотрудник Кусый Назар, web студия"emps.remove(obj4);// удаляем сотрудника obj4alert(emps);// "0: Сотрудник Танасийчук Степан, web студия"
NamespacePatternsInterfaceISubf()Subg()EndInterfaceClassAImplementsIPublicSubf()ImplementsI.fSystem.Console.WriteLine("A: вызываем метод f()")EndSubPublicSubg()ImplementsI.gSystem.Console.WriteLine("A: вызываем метод g()")EndSubEndClassClassBImplementsIPublicSubf()ImplementsI.fSystem.Console.WriteLine("B: вызываем метод f()")EndSubPublicSubg()ImplementsI.gSystem.Console.WriteLine("B: вызываем метод g()")EndSubEndClassClassCImplementsI' Создаём объект, методы которого будем делегироватьPrivateiAsI=NewA()PublicSubf()Implementsi.fi.f()EndSubPublicSubg()Implementsi.gi.g()EndSub' Этими методами меняем поле-объект, чьи методы будем делегироватьPublicSubtoA()i=NewA()EndSubPublicSubtoB()i=NewB()EndSubEndClassClassDelegatePatternSharedSubMain()DimcAsNewC()c.f()c.g()c.toB()c.f()c.g()System.Console.ReadKey()EndSubEndClassEndNamespace
Нетривиальный пример
Исходный текст на VB.NET
ImportsSystem.Collections.GenericImportsSystem.LinqImportsSystem.TextNamespaceEmployees''' <summary>''' Класс для хранения данных о сотруднике.''' </summary>ClassEmployeePrivatem_nameAsStringPrivatem_departmentAsStringPublicSubNew(ByValnameAsString,ByValdepartamentAsString)Me.m_name=nameMe.m_department=departamentEndSub''' <summary>''' Имя сотрудника.''' </summary>PublicReadOnlyPropertyName()AsStringGetReturnMe.m_nameEndGetEndProperty''' <summary>''' Отдел работы.''' </summary>PublicReadOnlyPropertyDepartment()AsStringGetReturnMe.m_departmentEndGetEndPropertyEndClass''' <summary>''' Класс для хранения списка сотрудников.''' </summary>ClassEmployeesListPrivateemployeesAsNewList(OfEmployee)()''' <summary>''' Свойство для получения и записи сотрудника по индексу.''' </summary>''' <param name="index">Индекс сотрудника.</param>''' <returns>Сотрудник.</returns>DefaultPublicPropertyItem(ByValindexAsInteger)AsEmployeeGetReturnemployees(index)EndGetSet(ByValvalueAsEmployee)employees(index)=valueEndSetEndProperty''' <summary>''' Добавление нового сотрудника.''' </summary>''' <param name="employee">Новый сотрудник.</param>PublicSubAdd(ByValemployeeAsEmployee)employees.Add(employee)EndSub''' <summary>''' Удаление существующего сотрудника.''' </summary>''' <param name="employee">Сотрудник для удаления.</param>PublicSubRemove(ByValemployeeAsEmployee)employees.Remove(employee)EndSub''' <summary>''' Последовательный поиск сотрудника по имени.''' </summary>''' <param name="name">Имя сотрудника.</param>''' <returns>Индекс сотрудника.</returns>PublicFunctionGetIndexOfEmployeeByName(ByValnameAsString)AsIntegerDimindexAsInteger=-1ForiAsInteger=0Toemployees.Count-1Ifemployees(i).Name=nameThenindex=iExitForEndIfNextReturnindexEndFunction''' <summary>''' Последовательный поиск сотрудника по имени.''' </summary>''' <param name="name">Имя сотрудника.</param>''' <param name="offset">Позиция, с которой следует начинать поиск.</param>''' <returns>Индекс сотрудника.</returns>PublicFunctionGetIndexOfEmployeeByName(ByValnameAsString,ByValoffsetAsInteger)AsIntegerDimindexAsInteger=-1ForiAsInteger=offsetToemployees.Count-1Ifemployees(i).Name=nameThenindex=iExitForEndIfNextReturnindexEndFunctionEndClassClassProgramSharedSubMain()'Создание списка сотрудников и добавление записей в негоDimempListAsNewEmployeesList()empList.Add(NewEmployee("Шлёнский Дмитрий","web студия"))empList.Add(NewEmployee("Кусый Назар","web студия"))empList.Add(NewEmployee("Сорока Орест","web студия"))'Поиск сотрудника Кусый Назар и вывод результата при поиске с начала и со 2-й позицииConsole.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар").ToString())Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар",2).ToString())'Поиск и удаление сотрудника Сорока ОрестempList.Remove(empList(empList.GetIndexOfEmployeeByName("Сорока Орест")))Console.Read()EndSubEndClassEndNamespace