Практический
пример: специализированная сортировка
Предыдущие примеры выглядят искусственно и относятся к категории «игрушечных программ». В этом разделе мы покажем, как использовать делегаты при специализированной сортировке — одной из стандартных областей применения функций обратного вызова. Общая идея заключается в том, что один метод сортировки в зависимости от ситуации может использовать разные критерии сортировки. Предположим, у вас имеется массив имен: «Mike Item», «Dave Mendlen», «Alan Carter», «Tony Goodhew», «Ari Bixhorn», «Susan Warren»-.
Если вызвать
метод Sort класса Array, сортировка будет произведена по именам. А если вы хотите
отсортировать массив по фамилиям?
Возможный
подход к решению этой задачи описан в главе 5 — вы можете написать собственную
реализацию интерфейса IComparer и передать ее Sort. Применение обратного вызова
на базе делегатов выглядит чуть более элегантно и теоретически обладает большей
гибкостью, поскольку программист может определить собственные процедуры сортировки,
которые в конкретной ситуации работают эффективнее стандартных алгоритмов.
Чтобы массив
поддерживал сортировку по именам, следует определить класс с несколькими методами
Compare и при помощи делегата связать алгоритм сортировки с нужным методом Compare
через механизм обратного вызова. В частности, это позволит динамически изменять
критерий сортировки во время работы программы.
Прежде всего
определяется класс, выполняющий сортировку. Чтобы избежать подробного обсуждения
различных алгоритмов сортировки, мы воспользуемся простейшим алгоритмом волновой
сортировки:
- Начать с первого элемента.
- Последовательно просмотреть
все остальные элементы. Если очередной элемент окажется меньше текущего первого
элемента, поменять их местами.
- Начать со второго
элемента, просмотреть все остальные элементы.
- Продолжать до последнего
элемента. Основной код волновой сортировки выглядит так:
For i =bottom
To (top - bottom) For j =i + 1 To top
If Stuff(j) < Stuff(i))Then
temp = Stuff(i)
Stuff(i) = Stuff(j)
Stuff(j) = temp
End If
Next j
Next I
Чтобы реализовать
этот алгоритм с применением функций обратного вызова, необходимо определить
класс Special Sort с делегатом, используемым при обратном вызове. Код этого
класса приведен ниже:
1 Public Class
Special Sort
2 ' Определение
делегата
3 Public Delegate Function SpecialCompareCallback(ByVal flrstString _
As String,ByVal
secondString As String) As Boolean
4 ' Определение
процедуры, вызываемой делегатом
5 Public Shared Sub IfySort(ByVal Stuff As String()._
ByVal MyCompare
As SpecialCompareCallback)
6 Dim i, j As
Integer
7 Dim temp As
String
8 Dim bottom
As Integer = Stuff.GetLowerBound(0)
9 Dim top As
Integer = Stuff.GetUpperBound(0)
10 For i = bottom
To (top = bottom)
11 For j = i
+ 1 To top
12 If MyCompare(Stuff(j).
Stuff(i)) Then
13 temp = Stuff(i)
14 Stuff(1)
- Stuff (j)
15 Stuff(j)
= temp
16 End If
17 Next j
18 Next i
19 End Sub
20 End Class
В строке
З определяется делегат, при помощи которого классу передается информация об
используемом порядке сортировки. Делегат может инкапсулировать любую функцию,
которая, как и все нормальные функции сравнения строк, получает два строковых
параметра и возвращает логическую величину.
В строке
5 определяется общая процедура, одним из параметров которой является переменная
с типом делегата. Таким образом, в ключевой строке 12:
If MyCompare(Stuff(j).
Stuff(i)) Then
функция сравнения,
инкапсулированная в делегате MyCompare, может относиться к другому классу! Например,
если определить приведенный ниже класс, эта схема позволит использовать любой
из его методов Compare (обратите внимание: методы Compare объявлены общими,
поэтому для их вызова нам даже не нужно создавать конкретный экземпляр класса):
Public Class
MyCustomCompare
Public Shared
Function TheBasicComparetByVal firstString As String,
ByVal secondString As String) As Boolean
Return (firstString <- secondString)
End Function
Public Shared
Function TheSpecialCompare(ByVal firstString As String.
ByVal secondString As String)As Boolean Dint tokensl,tokens2 As String()
tokensl = firstString.Split(Chr(32))
tokens2 = secondString.Split(Chr(32))
Return (tokensl(l) <- tokens2(l))
' Сравнение по фамилии!
End Function
End Class
Класс содержит
две общие функции, которые ниже будут использованы для создания делегатов. Первая
функция, TheBasicCompare, просто сравнивает строки в алфавитном порядке. Более
интересная функция TheSpecialCompare предполагает, что строка передается в формате
«имя фамилия», и сравнивает фамилии, выделяя их при помощи удобной
функции Split.
Остается
лишь создать экземпляры класса SpecialSort и делегаты. Это происходит в следующей
функции Main (ключевые строки выделены жирным шрифтом):
1 Module Modulel
2 Sub Main()
3 Dim test()As
String ={"Mike Iem"."Dave Mendlen"."Alan Carter".
4 "Tony
Goodhew","An Bixhorn"."Susan Warren"}
5 ' Объявить
переменную обратного вызова в форме класс.делегат
6 Dim MyCallBack
As Special Sort.SpecialCompareCal1back
7 MyCallBack
= AddressOf MyCustomCompare.TheBasicCompare
8 SpecialSort.MySort(test,MyCallBack)
9 Console.WriteLine("Here
is a basic sort by FIRST name")
10 Dim temp
As String
11 For Each
temp In test
12 Console.WriteLine(temp)
13 Next
14 ' Передать
другую процедуру сравнения
15 MyCallBack
= AddressOf MyCustomCompare.TheSpecialCompare
16 Sped al Sort.
MySort (test. MyCallBack)
17 Console.WriteLine()
18 Console.WriteLineC'Here
is a sort by LAST name")
19 For Each
temp In test
20 Console.WriteLine(temp)
21 Next
22 Console.
ReadLine()
23 End Sub
24 End Module
В строке
6 объявляется «псевдоуказатель на функцию». Чтобы задать его значение,
мы передаем адрес функции с правильной сигнатурой (строки 7-15). Поскольку функции
объявлены общими, создавать экземпляр класса MyCustomCompare для этого не нужно.
После создания делегата в строках 8 и 16 вызывается нужная процедура сортировки
класса Special Sort. Поскольку при вызове MySort передается делегат, процедура
обращается к классу MyCustomCompare и узнает, по какому критерию должно осуществляться
сравнение.