Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
94
12.1.2 Delegate tĩnh
Điểm bất lợi của ví dụ 12-1 là nó buộc lớp gọi, trong trường hợp này là lớp
Test
,
phải khởi tạo các
delegate
nó cần để sắp thứ tự các đối tượng trong một cặp. Sẽ
tốt hơn nếu như có thể lấy các
delegate
từ lớp
Dog
và
Student
. Chúng ta có thể
làm điều này bằng cách tạo cho trong mỗi lớp một
delegate
tĩnh. Đối với lớp
Student
ta thêm như sau:
public static readonly Pair.WhichIsFirst OrderStudents =
new Pair.WhichIsFirst(Student.WhichStudentComesFirst);
Dòng lệnh này tạo một
delegate
tĩnh, chỉ đọc có tên là
OrderStudent
Ta có thể tạo tương tự cho lớp
Dog
public static readonly Pair.WhichIsFirst OrderDogs =
new Pair.WhichIsFirst(Dog. WhichDogComesFirst);
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
95
12.1.4 Thứ tự thực thi với mảng các các delegate
Delegate
có thể giúp ta xậy dựng một hệ thống cho phép người dùng có thể quyết
định một cách động trình tự thực thi các thao tác. Giả sử chúng ta có hệ thống sử lý
ảnh, hệ thống này có thể thao tác ảnh theo nhiều cách như: làm mờ (blur) ảnh, làm
sắc nét, quay, lọc v.v…ảnh. Cũng giả sử rằng trình tự áp dụng các hiệu ứng trên ảnh
hưởng lớn đến đến chất lượng của ảnh. Người dùng sẽ mong muốn chọn các hiệu
ứng họ lẫn trình tự của chúng từ một thực đơn, sau đó hệ thống sẽ thực hiện các
hiệu ứng này theo trình tự họ đã định.
dùng hay không. Ta có thể cải tiến bằng cách thay đổi biến thành viên tĩnh thành
delagate
Một vấn đề với tĩnh là nó phải được khởi tạo trước, cho dù có được
12.1.3 Delegate như Property
Kết quả hoàn toàn như ví dụ trên.
dogPair.ToString( ));
Console.WriteLine("After ReverseSort
dogPair.ReverseSort(Dog.OrderDogs);
dogPair.ToString( ));
Console.WriteLine("After Sort dogPair\t\t: {0}",
dogPair.Sort(Dog.OrderDogs);
studentPair.ToString( ));
Console.WriteLine("After ReverseSort studentPair\t: {0}",
studentPair.ReverseSort(Student.OrderStudents);
studentPair.ToString( ));
Console.WriteLine("After Sort studentPair\t\t: {0}",
studentPair.Sort(theStudentDelegate);
truyền như tham số.
Như vậy mỗi lớp có một delegate riêng, khi cần ta lấy các delegate này và
}
get{ return new Pair.WhichIsFirst(WhichDogComesFirst);}
{
public static Pair.WhichIsFirst OrderDogs
Dog
Tương tự thay thế cho lớp
}
get{ return new Pair.WhichIsFirst(WhichStudentComesFirst); }
{
public static Pair.WhichIsFirst OrderStudents
và thay thế bằng
new Pair.WhichIsFirst(Student.WhichStudentComesFirst);
public static readonly Pair.WhichIsFirst OrderStudents =
, ta bỏ khai báo sau:
Student
Đối với lớp
property
delegate
Khác biệt chính ở đây là sẽ chỉ được khởi tạo khi có yêu cầu.
return new Pair.WhichIsFirst(WhichStudentComesFirst);
OrderStudent delegate
Khi property được truy cập, sẽ được tạo:
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
96
Ta có thể tạo một delegate cho mỗi thao tác (hiệu ứng) và đẩy chúng vào một túi
chứa có thứ tự, như một mảng chẳng hạn, theo đúng trình tự nó sẽ được thực thi.
Khi tất cả các
delegate
được tạo và thêm vào túi chứa, ta chỉ đơn giản duyệt suốt
mảng, gọi các
delegate
khi tới lượt.
Ta bắt đầu tạo lớp
Image
để đại diện cho một bức ảnh sẽ được xử lý bởi
ImageProcessor
:
public class Image
{
public Image( )
{
Console.WriteLine("An image created");
}
}
Lớp
ImageProcessor
khai báo một
delegate
không tham số và trả về kiểu
void
public delegate void DoEffect( );
Sau đó khai báo một số phương thức để thao tác ảnh có nguyên mẫu hàm như
delegate
đã khai báo ở trên.
public static void Blur( )
{
Console.WriteLine("Blurring image");
}
public static void Filter( )
{
Console.WriteLine("Filtering image");
}
public static void Sharpen( )
{
Console.WriteLine("Sharpening image");
}
public static void Rotate( )
{
Console.WriteLine("Rotating image");
}
Lớp
ImageProccessor
cần một mảng để giữ các
delegate
người dùng chọn;
một biến để giữ số lượng hiệu ứng muốn xử lý và hiển nhiên một bức ảnh
image
DoEffect[] arrayOfEffects;
Image image;
int numEffectsRegistered = 0;
ImageProccessor
cũng cần một phương thức để thêm
delegate
vào mảng
public void AddToEffects(DoEffect theEffect)
{
if (numEffectsRegistered >= 10)
{
throw new Exception("Too many members in array");
}
arrayOfEffects[numEffectsRegistered++] = theEffect;
}
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
97
Một phương thức để gọi thực thi các hiệu ứng
public void ProcessImages( )
{
for (int i = 0;i < numEffectsRegistered;i++)
{
arrayOfEffects[i]( );
}
}
Cuối cùng ta khai báo các
delegate
tĩnh để
client
có thể gọi.
public DoEffect BlurEffect = new DoEffect(Blur);
public DoEffect SharpenEffect = new DoEffect(Sharpen);
public DoEffect FilterEffect = new DoEffect(Filter);
public DoEffect RotateEffect = new DoEffect(Rotate);
Client
sẽ có các đoạn mã để tương tác với người dùng, nhưng chúng ta sẽ làm lơ
chuyện này, mặc định các hiệu ứng, thêm chúng vào mảng và sau đó gọi
ProcessImage
Ví dụ 12-2. Sử dụng mảng các deleage
using System;
namespace Programming_CSharp
{
// ảnh ta sẽ thao tác
public class Image
{
public Image( )
{
Console.WriteLine("An image created");
}
}
public class ImageProcessor
{
// khai báo delegate
public delegate void DoEffect( );
// tạo các delegate tĩnh gắn với các phương thức thành viên
public DoEffect BlurEffect = new DoEffect(Blur);
public DoEffect SharpenEffect = new DoEffect(Sharpen);
public DoEffect FilterEffect = new DoEffect(Filter);
public DoEffect RotateEffect = new DoEffect(Rotate);
// hàm dựng khởi tạo ảng và mảng
public ImageProcessor(Image image)
{
this.image = image;
arrayOfEffects = new DoEffect[10];
}
public void AddToEffects(DoEffect theEffect)
{
if (numEffectsRegistered >= 10)
{
throw new Exception( "Too many members in array" );
}
arrayOfEffects[numEffectsRegistered++] = theEffect;
}
// các hiệu ứng ảnh
public static void Blur( )
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
98
{
Console.WriteLine("Blurring image");
}
public static void Filter( )
{
Console.WriteLine("Filtering image");
}
public static void Sharpen( )
{
Console.WriteLine("Sharpening image");
}
public static void Rotate( )
{
Console.WriteLine("Rotating image");
}
public void ProcessImages( )
{
for (int i = 0;i < numEffectsRegistered;i++)
{
arrayOfEffects[i]( );
}
}
// các biến thành viên
private DoEffect[] arrayOfEffects;
private Image image;
private int numEffectsRegistered = 0;
}
// lớp kiểm thử
public class Test
{
public static void Main( )
{
Image theImage = new Image( );
// không giao diện để làm đơn giản vấn đề
ImageProcessor theProc = new ImageProcessor(theImage);
theProc.AddToEffects(theProc.BlurEffect);
theProc.AddToEffects(theProc.FilterEffect);
theProc.AddToEffects(theProc.RotateEffect);
theProc.AddToEffects(theProc.SharpenEffect);
theProc.ProcessImages( );
}
}
}
Kết quả:
An image created
Blurring image
Filtering image
Rotating image
Sharpening image
Trong lớp
Test
,
ImageProcessor
được khởi tạo và các hiệu ứng được thêm vào.
Nếu người dùng chọn làm mờ ảnh (blur) trước khi lọc ảnh (filter), chỉ cần đơn giản
thay đổi thứ tự của chúng trong mảng Tương tự, bất kỳ một hiệu ứng nào cũng có
thể được lặp lại bằng cách thêm vào túi chứa
delegate
nhiều lần.
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
99
12.1.5 Multicasting
Multicasting
là cách để gọi hai phương thức thông qua một
delegate
đơn.
Điều này sẽ trở nên quan trọng khi quản lý các sự kiện. Mục tiêu chính là để có một
delegate
đơn có thể gọi nhiều phương thức cùng một lúc. Nó khác với mảng các
delagte
, trong mảng delegate mỗi
delegate
chỉ gọi một phương thức. Ví dụ
trước dùng một mảng làm túi chứa nhiều
delegate
khác nhau.
Với
multicasting
ta có thể tạo một
delegate
đơn đóng gói nhiều phương thức.
Ví dụ khi một
button
được nhấn, ta hằn muốn thao tác nhiều hành động cùng một
lúc. Ta có thể cài đặt điều này bằng cách cho mỗi
button
một mảng các
delegate
, nhưng sẽ dễ hơn và rõ nghĩa hơn khi tạo một
multicasting
delegate
.
Bất kỳ một
delegate
nào trả về
void
đều là
multicast
delegate
, mặc dù ta
có thể đối xử nó như
single
cast
delegate
(là delegate đề cập ở phần trên) nếu
muốn. Hai
multicast
delegate
có thể kết nối với nhau bằng toán tử cộng (+).
Kết quả là một
multicast
delegate
mới đóng gói tất cả các phương thức của
hai
delegate
toán hạng. Ví dụ, giả sử
Writer
và
Logger
là các
delegate
trả về
kiểu
void
, dòng lệnh sau sẽ kết nối chúng và tạo ra một
multicast
delegate
mới có tên là
myMulticastDelegate
myMulticastDelegate = Writer + Logger;
Ta cũng có thể thêm một
delegate
vào một
multicast
delegate
bằng toán tử
cộng bằng (+=). Giả sử ta có
Transmitter
và
myMulticastDelegate
là các
delegate
, dòng lệnh sau:
myMulticastDelegate += Transmitter;
tương tự như dòng:
myMulticastDelegate = myMulticastDelegate + Transmitter;
Để xem cách
multicast
delegate
được tạo và sử dụng, xem qua toàn bộ ví dụ
12-3. Trong ví dụ này ta tạo một lớp tên là
MyClassWithDelegate
, lớp này định
nghĩa một
delegate
nhận một tham số kiểu chuỗi và trả về kiểu
void
.
public delegate void StringDelegate(string s);
Sau đó ta định nghĩa một lớp tên là
MyImplementingClass
có ba phương thức, tấ
cả đều trả về
void
và nhận một tham số kiểu chuỗi:
WriteString
,
LogString
và
TransmitString. Phương thức đầu viết một chuỗi ra màn hình (đầu ra chuẩn),
phương thức thứ hai viết ra tập tin lỗi (log file) và phương thức thứ ba chuyển chuỗi
lên Internet. Ta tạo các delegate để gọi các phương thức thích hợp.
Writer("String passed to Writer\n");
Logger("String passed to Logger\n");
Transmitter("String passed to Transmitter\n");
Để xem cách kết hợp các
delegate
ta tạo ra một
delegate
khác
MyClassWithDelegate.StringDelegate myMulticastDelegate;
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
100
và gán nó bằng kết quả của phép cộng hai delegate đã tồn tại
myMulticastDelegate = Writer + Logger;
Ta cũng có thể thêm bằng toán tử cộng bằng
myMulticastDelegate += Transmitter;
Cuối cùng ta có thể bỏ một
delegate
bằng toán tử trừ bằng (-=)
DelegateCollector -= Logger;
Ví dụ 12-3. Kết hợp các delegate
using System;
namespace Programming_CSharp
{
public class MyClassWithDelegate
{
// khai báo delegate
public delegate void StringDelegate(string s);
}
public class MyImplementingClass
{
public static void WriteString(string s)
{
Console.WriteLine("Writing string {0}", s);
}
public static void LogString(string s)
{
Console.WriteLine("Logging string {0}", s);
}
public static void TransmitString(string s)
{
Console.WriteLine("Transmitting string {0}", s);
}
}
public class Test
{
public static void Main( )
{
// định nghĩa ba đối tượng StringDelegate
MyClassWithDelegate.StringDelegate Writer,Logger,Transmitter;
// định nghĩa một SringDelegate khác
// hành động như một multicast delegate
MyClassWithDelegate.StringDelegate myMulticastDelegate;
// khởi tạo 3 delegate đầu tiên,
// truyền vào các phương thức định đóng gói
Writer = new MyClassWithDelegate.StringDelegate(
MyImplementingClass.WriteString);
Logger = new MyClassWithDelegate.StringDelegate(
MyImplementingClass.LogString);
Transmitter = new MyClassWithDelegate.StringDelegate(
MyImplementingClass.TransmitString);
// gọi phương thức của delegate Writer
Writer("String passed to Writer\n");
// gọi phương thức của delegate Logger
Logger("String passed to Logger\n");
// gọi phương thức của delegate Transmitter
Transmitter("String passed to Transmitter\n");
// thông báo kết nối hai deleagte
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
101
// thành một multicast deleagte
Console.WriteLine("myMulticastDelegate = Writer + Logger");
// kết nối hai deleagte
// thành một multicast deleagte
myMulticastDelegate = Writer + Logger;
// gọi delegated, hai phương thức được gọi
myMulticastDelegate("First string passed to Collector");
// thông báo thêm deleagte thứ ba
// vào một multicast deleagte
Console.WriteLine("\nmyMulticastDelegate += Transmitter");
// thêm delegate thứ ba
myMulticastDelegate += Transmitter;
// gọi delegate, ba phương thức được gọi
myMulticastDelegate("Second string passed to Collector");
// thông báo loại bỏ delegate logger
Console.WriteLine("\nmyMulticastDelegate -= Logger");
// bỏ delegate logger
myMulticastDelegate -= Logger;
// gọi delegate, hai phương htức còn lại được gọi
myMulticastDelegate("Third string passed to Collector");
}
}
}
Kết quả:
Writing string String passed to Writer
Logging string String passed to Logger
Transmitting string String passed to Transmitter
myMulticastDelegate = Writer + Logger
Writing string First string passed to Collector
Logging string First string passed to Collector
myMulticastDelegate += Transmitter
Writing string Second string passed to Collector
Logging string Second string passed to Collector
Transmitting string Second string passed to Collector
myMulticastDelegate -= Logger
Writing string Third string passed to Collector
Transmitting string Third string passed to Collector …
Sức mạnh của
multicast
delegate
sẽ dễ hiểu hơn trong khái niệm
event
.