Tải bản đầy đủ (.pdf) (15 trang)

Tài liệu Cơ chế ủy quyền và sự kiện phần cuối docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (196.24 KB, 15 trang )

public void Run()

{


for(;;)

{


// ngừng 10 giây

Thread.Sleep( 10 );





// lấy thời gian hiện hành

System.DateTime dt = System.DateTime.Now;

// nếu giây thay đổi cảnh báo cho subscriber
if ( dt.Second != second)
{


// tạo TimeInfoEventArgs để truyền

// cho subscriber


TimeInfoEventArgs timeInformation =

new TimeInfoEventArgs( dt.Hour, dt.Minute, dt.Second);

// nếu có bất cứ lớp nào đăng ký thì cảnh báo
if ( OnSecondChange != null)
{


OnSecondChange( this, timeInformation);

}

}


// cập nhật trạng thái
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
}

}


Phương thức Run tạo vòng lặp vô hạn để kiểm tra định kỳ thời gian hệ thống. Nếu
thời gian thay đổi từ đối tượng Clock hiện hành, thì nó sẽ cảnh báo cho tất cả các
subscriber và sau đó cập nhật lại những trạng thái của nó.
Bước đầu tiên là ngừng 10 giây:


Thread.Sleep(10);
Ở đây chúng ta sử dụng phương thức tĩnh của lớp Thread từ System.Threading của .NET.
Sử dụng ph
ương thức Sleep() để kéo dài khoảng cách giữa hai lần thực hiện vòng lặp.
Sau khi ngừng 10 mili giây, phương thức sẽ kiểm tra thời gian hiện hành:

System.DateTime dt = System.DateTime.Now;
Cứ khoảng 100 lần kiểm tra, thì một giây sẽ được gia tăng. Phương thức ghi nhận sự
thay đổi
và cảnh báo đến những subscriber của nó. Để làm được điều này, đầu tiên phải tạo ra
một đối tượng TimeInfoEventArgs:
if ( dt.Second != second)

{


// tạo TimeInfoEventArgs để truyền cho các subscriber

TimeInfoEventArgs timeInformation =

new TimeInfoEventArgs( dt.Hour, dt.Minute, dt.Second);


}
Và để cảnh báo cho những subscriber bằng cách kích hoạt sự kiện OnSecondChange:
// cảnh báo cho các subscriber

if ( OnSecondChange != null)

{



OnSecondChange( this, timeInformation);

}


Nếu một sự kiện không có bất cứ lớp subscriber nào đăng ký thì nó ước lượng giá trị là
null. Phần kiểm tra bên trên xác định giá trị của sự kiện có phải là null hay không, để đảm
bảo rằng
có tồn tại lớp đăng ký nhận sự kiện trước khi gọi sự kiện OnSecondChange.
Chúng ta lưu ý rằng OnSecondChange lấy hai tham số: nguồn phát ra sự kiện và đối
tượng dẫn xuất từ lớ
p EventArgs. Ở đây chúng ta có thể thấy rằng tham chiếu this
của lớp clock được truyền bởi vì clock là nguồn phát ra sự kiện. Tham số thứ hai là
đối tượng TimeInfo- EventArgs được tạo ra ở dòng lệnh bên trên.
Một sự kiện được phát ra thì sẽ gọi bất cứ phương thức nào được đăng ký với lớp Clock
thông qua delegate, chúng ta sẽ kiểm tra điều này sau.
Một khi mà sự kiện được phát ra, chúng ta sẽ cập nhật lạ
i trạng thái của lớp Class:

this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
Sau cùng là chúng ta xây dựng những lớp có thể đăng ký vào các sự kiện này. Chúng ta
sẽ tạo
hai lớp. Lớp đầu tiên là lớp DisplayClock. Chức năng chính của lớp này không phải là
lưu giữ thời gian mà chỉ để hiển thị thời gian hiện hành ra màn hình console. Để đơn
giản chúng ta
chỉ tạo hai phương thức cho lớp này. Phương thức thứ nhất có tên là Subscribe,

phương thức chị
u trách nhiệm đăng ký một sự kiện OnSecondChange của lớp Clock.
Phương thức thứ hai được tạo ra là trình xứ lý sự kiện TimeHasChanged:
public class DisplayClock

{


public void Subscrible(Clock theClock)

{


theClock.OnSecondChange +=

new Clock.SecondChangeHandler(TimeHasChanged);

}


public void TimeHasChanged( object theClock, TimeInfoEventArgs ti)

{


Console.WriteLine(“Current Time:
{0]:{1}:{2}”, ti.hour.ToString(),
ti.minute.ToString(),
ti.Second.ToString());






}

}


Khi phương thức đầu tiên Subscribe được gọi, nó sẽ tạo ra một delegate
SecondChange- Handler mới, và truyền vào phương thức xử lý sự kiện
TimeHasChanged của nó. Sau đó nó sẽ đăng ký delegate với sự kiện OnSecondChange
của Clock.
Lớp thứ hai mà chúng ta tạo cũng sẽ đáp ứng sự kiện này, tên là LogCurrentTime.
Thông thường lớp này ghi lại sự kiện vào trong tập tin, nhưng với mục đích minh họa
của chúng ta, nó sẽ ghi ra màn hình console:

public class LogCurrentTime

{


public void Subscribe(Clock theClock)

{


theClock.OnSecondChange +=

new Clock.SecondChangeHandler(WriteLogEntry);


}


// thông thường phương thức này viết ra file

// nhưng trong minh họa này chúng ta chỉ xuất

// ra màn hình console mà thôi

public void WriteLogEntry( object theClock, TimeInfoEventArgs ti)

{


Console.WriteLine(“Logging to file:
{0}:{1}:{2}”, ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}

}


Ghi chú rằng những sự kiện được thêm vào bằng cách sử dụng toán tử +=. Điều này cho
phép những sự kiện mới được thêm vào sự kiện OnSecondChange của đối tượng Clock
mà không
có phá hủy bất cứ sự kiện nào đã được đăng ký. Khi LogCurrentTime đăng ký một
sự kiện OnSecondChange, chúng ta không muốn việc đăng ký này làm mất đi sự
đăng ký của lớp DisplayClock trước đó.

Tất cả ph
ần còn lại cần thực hiện là tạo ra một lớp Clock, tạo mộ đối tượng
DisplayClock và bảo nó đăng ký sự kiện. Sau đó chúng ta tạo ra một đối tượng
LogCurrentTime và cũng đăng
ký sự kiện tương tự. Cuối cùng thì thực thi phương thức Run của Clock. Tất cả phần
trên được trình bày trong ví ụ 11.4.
Ví dụ 11.4: làm việc với những sự kiện.


namespace Programming_CSharp

{

using System;

using System.Threading;

// lớp lưu giữ thông tin về sự kiện, trong trường hợp

// này nó chỉ lưu giữ những thông tin có giá trị lớp
clock public class TimeInfoEventArgs : EventArgs
{


public TimeInfoEventArgs(int hour, int minute, int second)

{

this.hour = hour;
this.minute = minute;

this.second = second;
}

public readonly int hour;
public readonly int minute;
public readonly int second;
}


// khai báo lớp Clock lớp này sẽ phát ra các sự kiện
public class Clock
{


// khai báo delegate mà các subscriber phải thực thi

public delegate void SecondChangeHandler(object
clock, TimeInfoEventArgs timeInformation);
// sự kiện mà chúng ta đưa ra

public event SecondChangeHandler OnSecondChange;

// thiết lập đồng hồ thực hiện, sẽ phát ra mỗi sự kiện trong mỗi giây
public void Run()
{


for(;;)

{



// ngừng 10 giây

Thread.Sleep( 10 );

// lấy thời gian hiện hành

System.DateTime dt = System.DateTime.Now;

// nếu giây thay đổi cảnh báo cho subscriber
if ( dt.Second != second)
{


// tạo TimeInfoEventArgs để truyền



// cho subscriber

TimeInfoEventArgs timeInformation =

new TimeInfoEventArgs( dt.Hour, dt.Minute, dt.Second);

// nếu có bất cứ lớp nào đăng ký thì cảnh báo
if ( OnSecondChange != null)
{



OnSecondChange( this, timeInformation);

}

}


// cập nhật trạng thái
this.second = dt.Second;
this.minute = dt.Minute;
this.hour = dt.Hour;
}

}


private int hour;
private int minute;
private int second;
}


// lớp DisplayClock đăng ký sự kiện của clock.

// thực thi xử lý sự kiện bằng cách hiện thời gian hiện hành
public class DisplayClock
{


public void Subscrible(Clock theClock)


{


theClock.OnSecondChange +=

new Clock.SecondChangeHandler(TimeHasChanged);

}


public void TimeHasChanged( object theClock, TimeInfoEventArgs ti)

{


Console.WriteLine(“Current Time:
{0}:{1}:{2}”, ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}

}


// lớp đăng ký sự kiện thứ hai public class LogCurrentTime



{


public void Subscribe(Clock theClock)

{


theClock.OnSecondChange +=

new Clock.SecondChangeHandler(WriteLogEntry);

}


// thông thường phương thức này viết ra file

// nhưng trong minh họa này chúng ta chỉ xuất

// ra màn hình console mà thôi

public void WriteLogEntry( object theClock, TimeInfoEventArgs ti)

{


Console.WriteLine(“Logging to file:
{0}:{1}:{2}”, ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}


}


// lớp Test minh họa sử dụng sự kiện
public class Test
{

public static void Main()

{

// tạo ra đối tượng clock

Clock theClock = new Clock();

// tạo đối tượng DisplayClock đăng ký

// sự kiện và xử lý sự kiện

DisplayClock dc = new DisplayClock();

dc.Subscribe(theClock);

// tạo đối tượng LogCurrent và yêu cầu đăng

// ký và xử lý sự kiện

LogCurrentTime lct = new LogCurrentTime();

lct.Subscribe(theClock);


// bắt đầu thực hiện vòng lặp và phát sinh sự kiện

// trong mỗi giây đồng hồ
theClock.Run();
}

}

}






Kết quả thực hiện có
thể như sau:

Current Time: 11:54:20

Logging to file: 11:54:20

Current Time: 11:54:21

Logging to file: 11:54:21

Current Time: 11:54:22

Logging to file: 11:54:22




Điều quan trọng chính
của ví dụ minh họa trên là
việc tạo ra hai lớp đối
tượng DisplayClock
và lớp LogCurrentTime.
Cả hai lớp này đều đăng
ký một sự kiện
Clock.OnSecondChange
của lớp thứ ba là lớp
Clock
Lợi ích của cơ chế
publish/subscribe là bất
cứ lớp nào cũng có thể
được cảnh báo khi một
sự kiện xuất hiện.
Những lớp subscriber
không cần biết cách mà
Clock làm việc, và
Clock cũng không cần
biết cách mà các lớp
subscriber đáp ứng với
sự kiện mà nó đưa ra.
Publisher và subscriber
được
phân
tách
bởi

deleg
ate,
đây là
một
sự
mong
đợi
cao,

làm
cho

lện
h
lin
h
họ
at

mạ
nh
mẽ

n.
Lớ
p
Cl
oc
k


thể
tha
y
đổi
các
h
dò thời gian mà không
làm ảnh hưởng đến bất
cứ lớp subscriber nào.
Các lớp subscriber có
thể thay đổi cách mà
chúng đáp ứng với sự
thay đổi của thời gian
mà không tác động với
Clock. Cả hai lớp này
hoạt động độc lập với
nhau, và làm cho đoạn
chương trình dễ duy trì
hơn.
Câu hỏi và trả lời

Câuhỏi
1: Tóm tắt những
nét cơ bản về uỷ quyền?
Trả lời 1
: Ủy quyền là
một kiểu dữ liệu tham
chiếu đươc dùng để
đóng gói phương thức
với các tham số và kiểu

trả về xác định. Ủy
quyền cũng tương tự
như con trỏ hàm trong
ngôn ngữ C++. Tuy
nhiên, trong ngôn ngữ
C# ủy quyền là kiểu dữ
liệu hướng đối tượng,
an toàn
và bảo mật.
Câuhỏi
2: Con trỏ hàm là
gì?
Trả l ời 2
: Trong ngôn ngữ
như C hay C++, có một
chức năng gọi là con trỏ
hàm. Một con
trỏ hàm được sử dụng
để thiết lập cùng một
nhi
ệm
vụ
nh
ư
mộ
t
ủy
qu
yề
n.

Tu
y
nhi
ên,
co
n
trỏ

m
tro
ng
C/
C
+
+
đơ
n
giả
n
kh
ôn
g
ph
ải

mộ
t đối tượng. Còn ủy
quyền trong C# là kiểu
dữ liệu an toàn, được
dùng để tham chiếu đến

những phương thức, ủy
quyền còn được sử
dụng
bởi những sự kiện.

Câu hỏi thêm

Câu hỏi 1
: Có thể sử dụng
ủy quyền như một thuộc
tính hay không? Nếu có
thể thì sử dụng như thế
nào? Cho biết ý nghĩa?
Câu hỏi 2
: Nếu có một số
hoạt động cần được thực
hiện theo một thứ tự nhất
định thì ta phải
làm thế nào để khi cần
thực hiện thì gọi lần lượt
thực hiện các hoạt động
đó?

Câu hỏi 3
: Công dụng của
việc khai báo ủy quyền
tĩnh? Khi nào thì nên khai
báo ủy quyền tĩnh khi nào
thì không nên?
Câu hỏi 4

: Một ủy quyền có
thể gọi được nhiều hơn
một phương thức hay
không? Chức năng nào
trong C# hỗ trợ ủy quyền
này?
Câu hỏi 5
: Có phải tất cả
các ủy quyền đều là ủy
quyền Multicast hay
không? Điều kiện để trở
thành ủy quyền Multicast?
Câu
hỏi
6
:
Các
toán
tử
nào

thể
dùn
g để
thực
hiện
việc
Mul
tica
st

các
ủy
quy
ền?
C
â
u
h

i
7
:

S


k
i

n

l
à

g
ì? Trong hệ thống
ứng dụng nào thì sự
kiện được sử dụng
nhiều?
Câuhỏi

8: Những sự
kiện trong C# được
thực hiện thông qua
cái gì?
Câu hỏi 9
: Hãy tóm lược
quá trình tạo một sự kiện
và giải quyết sự kiện
thông qua cơ chế ủy quyền
trong C#?
Bài tập

Bài tập 1
: Viết chương
trình minh họa sử dụng
ủy quyền để thực hiện
việc sắp xếp các số
nguyên trong một mảng?
Bài tập 2
: Viết chương
trình minh họa sử dụng ủy
quyền để thực hiện việc
chuyển các ký tự thường
thành ký tự hoa trong một
chuỗi?
Bài tập 3
: Viết chương
trình kết hợp giữa
delegate và sự kiện để
minh họa một đồng hồ

điện
tử thể hiện giờ hiện hành
trên màn hình console.

×