【学习笔记】C# Delegates and Events - 讲的比较系统
2010-03-23 23:01
585 查看
This article—C# Delegates and Events— by Jeff Suddeth explains delegates and their applications in anonymous methods, asynchonous method calls, events, multicast delegates, threads, and Win32 callbacks.
In this article, I explain the many uses of delegates. I begin with a simple example using a delegate to invoke a method. Then, I show several other uses of delegates including multicast delegates, thread delegates, anonymous methods, asynchronous method calls, events, and Win32 callbacks.
委托就是一个封装方法的.NET类,但是和其他类封装方法的方式又是不一样的。委托实际上存储的是包含在其他类的方法的地址。因此,委托实际上相当于C++里的函数指针,但是它有不远只是函数指针。
multicast delegate 多播委托
thread delegate 线程托管
anonymous method 匿名方法
asynchronous method call 异步方法调用
event 事件
Win32 callback Win32函数调用
This example defines a
In this example, the
The next example defines two methods of the correct signature for our
This time, after creating the
So far, we have seen delegates used to call methods of other classes and objects. But, as I have already said, that is only the beginning. The fact that they can be bound to methods of any class or object gives them great utility. Delegates are central to .NET, as they are used in threads, events, and asynchronous programming. Now that we have seen how to create and use delegates, let’s take a look at some of their applications.
The
Now, let’s rewrite our thread example to use an anonymous method.
Since writing to disk is an expensive operation, we would like execute the
因为写入磁盘时一个费时的操作,我们可以异步执行FlushToDisk方法。
In the first asynchronous example, the
At the time our
这个时候CacheFlusher委托被声明了,编译器产生了2个方法,一个是BeginInvoke,一个是EndInvoke.
These two methods, respectively, are used to kick off a lengthy method call and then retrieve its return value when that method is finished. Rather than invoking the delegate as before,
The generated
这个产生的BeginInvoke方法会接受和已经声明的委托签名相同的参数类型。
The return value of
返回的BeginInvoke的值是一个对IAsyncResult接口的引用。 晚些,我们将要把这个值传给EndInvoke去得到这个异步方法的返回值。
The generated
当调用EndInvoke的时候,EndInvoke将会阻塞直到异步方法执行完。因此,为了有效地执行,我们需要寻找另外的方法去调用EndInvoke,而不至于阻塞线程的调用。
First we look at polling. The next code segment uses a
每循环一次检查下这个异步方法有没有完成。
The final method is to create an instance of the
最后一个方法就是创建一个AsyncCallback委托的实例,然后把它传送给BeginInvoke方法。
When you pass an
The
Whatever object you pass to the third argument of
class DelegateDemo {
static void Main(string[] args) {
Timer t = new Timer(1000);
t.Elapsed +=
new ElapsedEventHandler(Timer_Elapsed);
t.Enabled = true;
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
static void Timer_Elapsed(object sender, ElapsedEventArgs e) {
Console.WriteLine("tick");
}
}
[/code]
The
In this example, the method invoked by the
The next example uses the
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace enumchildren {
class WindowEnumerator {
// declare the delegate
public delegate bool WindowEnumDelegate (IntPtr hwnd,
int lParam);
// declare the API function to enumerate child windows
[DllImport("user32.dll")]
public static extern int EnumChildWindows(IntPtr hwnd,
WindowEnumDelegate del,
int lParam);
// declare the GetWindowText API function
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hwnd,
StringBuilder bld, int size);
static void Main(string[] args) {
// instantiate the delegate
WindowEnumDelegate del
= new WindowEnumDelegate(WindowEnumProc);
// call the win32 function
EnumChildWindows(IntPtr.Zero, del, 0);
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
public static bool WindowEnumProc(IntPtr hwnd,int lParam) {
// get the text from the window
StringBuilder bld = new StringBuilder(256);
GetWindowText(hwnd, bld, 256);
string text = bld.ToString();
if(text.Length > 0) {
Console.WriteLine(text);
}
return true;
}
}
}
[/code]
This example defines the
The
C# Delegates and Events
A delegate is a .NET class that encapsulates a method, but not in the same way other classes encapsulate methods. A delegate actually stores the address of a method that is contained in some other class. So, a delegate is really the equivalent of a function pointer in C++. However, they are also far more than that.In this article, I explain the many uses of delegates. I begin with a simple example using a delegate to invoke a method. Then, I show several other uses of delegates including multicast delegates, thread delegates, anonymous methods, asynchronous method calls, events, and Win32 callbacks.
委托就是一个封装方法的.NET类,但是和其他类封装方法的方式又是不一样的。委托实际上存储的是包含在其他类的方法的地址。因此,委托实际上相当于C++里的函数指针,但是它有不远只是函数指针。
multicast delegate 多播委托
thread delegate 线程托管
anonymous method 匿名方法
asynchronous method call 异步方法调用
event 事件
Win32 callback Win32函数调用
Declaring and using delegates
You declare a delegate in a class or namespace using thedelegatekeyword and by specifying the signature of the method it will call. The following line declares a delegate named
GreetingDelegatewhich will reference a method that returns
voidand accepts a string as the argument.
class DelegateDemo1 { static void Main(string[] args) { GreetingDelegate gd = new GreetingDelegate(SendGreeting); gd("Hello"); } static void SendGreeting(string s) { Console.WriteLine(s); } }
class Greeting { public void SendGreeting(string s) { Console.WriteLine(s); } } class DelegateDemo1 { static void Main(string[] args) { Greeting gr = new Greeting(); GreetingDelegate gd = new GreetingDelegate(gr.SendGreeting); gd ("Hello"); } }
This example defines a
Greetingclass that contains a single method named
SendGreeting. Because its signature matches that of the
GreetingDelegate, the method can be invoked through our delegate.
In this example, the
Mainmethod first instantiates the
Greetingclass. Then, it creates an instance of
GreetingDelegate, passing the
Greetingobject’s
SendGreetingmethod as argument. Finally,
Maincalls the object’s
SendGreetingmethod by invoking the delegate.
Multicast delegates
As I said in the introduction, delegates do far more than provide an indirect way to call a method. They also contain an internal list of delegates—called the invocation list—which contains delegates of the same method signature. Whenever the delegate is invoked, it also invokes each delegate in its invocation list. You add and remove delegates from an invocation list using the overloaded+=and
-=operators.
The next example defines two methods of the correct signature for our
GreetingDelegateand calls them both through a single call to an instance of the
GreetingDelegate.
class. The
In this example, we have added a second method to the [code]Greeting
SendGreetingToFilemethod does exactly as its name suggests. It creates a
StreamWriterinstance and uses it to log the string argument to a file named
"greeting.txt".
This time, after creating the
GreetingDelegate, the
Mainmethod instantiates a second
GreetingDelegateand appends it to the first instances’ invocation list through the
+=operator. When the last line of
Maincalls the delegate, both methods are invoked with the string
“Hello”.
So far, we have seen delegates used to call methods of other classes and objects. But, as I have already said, that is only the beginning. The fact that they can be bound to methods of any class or object gives them great utility. Delegates are central to .NET, as they are used in threads, events, and asynchronous programming. Now that we have seen how to create and use delegates, let’s take a look at some of their applications.
Thread delegates
Multithreaded programming in .NET is implemented through the use of delegates. The .NET Framework defines theThreadStartdelegate to invoke a method in a separate thread. The
Threadclass, defined in the
System.Threadingnamespace, accepts an instance of the
ThreadStartdelegate as its constructor argument. When you call the
Threadobject’s
Startmethod, that
Threadobject will invoke the delegate in its own thread of execution. The following example demonstrates this by calling a method named
ThreadProcin a separate thread.
class DelegateDemo { delegate void GreetingDelegate(string s); static void Main(string[] args) { // use an anonymous method for the delegate GreetingDelegate gd = delegate(string s) { Console.WriteLine(s); } // invoke the delegate gd("Hello"); } }
The
GreetingDelegateis declared as it was before. The interesting part is inside of the
Mainmethod when the
GreetingDelegateis instantiated with an anonymous method. The syntax might take a minute to get used to. The
delegatekeyword is followed by the parameter list that the delegate will accept. The parameter list is then followed by the block of code for the anonymous method. As before, the final line of the
Mainmethod invokes the anonymous method through the delegate instance.
Now, let’s rewrite our thread example to use an anonymous method.
args) { Thread t = new Thread(delegate() { // do some thread work CalculatePrimes()]class DataCache { public int FlushToDisk(string fileName) { // simulate a long operation Thread.Sleep(4000); return 0; } }
Since writing to disk is an expensive operation, we would like execute the
FlushToDiskmethod asynchronously. The next example defines the
CacheFlusherdelegate and then uses it to call the
DataCacheobject’s
FlushToDiskmethod asynchronously.
因为写入磁盘时一个费时的操作,我们可以异步执行FlushToDisk方法。
// invoke the method asynchronously IAsyncResult result = flusher.BeginInvoke("data.dat", null, null); // get the result of that asynchronous operation int retValue = flusher.EndInvoke(result); Console.WriteLine(retValue); } }
In the first asynchronous example, the
Mainmethod creates an instance of
DataCacheand encapsulates its
FlushToDiskmethod in an instance of the
CacheFlusherdelegate.
At the time our
CacheFlusherdelegate is declared, the compiler generates two methods for it named
BeginInvokeand
EndInvoke.
这个时候CacheFlusher委托被声明了,编译器产生了2个方法,一个是BeginInvoke,一个是EndInvoke.
These two methods, respectively, are used to kick off a lengthy method call and then retrieve its return value when that method is finished. Rather than invoking the delegate as before,
Mainexecutes the delegate as an asynchronous operation by calling its
BeginInvokemethod.
The generated
BeginInvokemethod will accept the same argument types as declared in the delegate’s signature, followed by some other arguments that we will ignore for now.
这个产生的BeginInvoke方法会接受和已经声明的委托签名相同的参数类型。
The return value of
BeginInvokeis a reference to an interface named
IAsyncResultwhich we later pass to
EndInvoketo receive the return value of the asynchronous method.
返回的BeginInvoke的值是一个对IAsyncResult接口的引用。 晚些,我们将要把这个值传给EndInvoke去得到这个异步方法的返回值。
The generated
EndInvokemethod is defined to return the same type as the asynchronous method. When called,
EndInvokewill block until the asynchronous method completes its execution. Therefore, to be efficient, we need to find other strategies for calling
EndInvokethat will not block the calling thread. Below, I show three techniques for calling
EndInvoke, polling for completion, using a wait handle, and using an
AsyncCallbackdelegate.
当调用EndInvoke的时候,EndInvoke将会阻塞直到异步方法执行完。因此,为了有效地执行,我们需要寻找另外的方法去调用EndInvoke,而不至于阻塞线程的调用。
First we look at polling. The next code segment uses a
whileloop to poll the
IsCompletedproperty of the
IAsyncResultobject. If the method call is not yet complete, then we can perform some other work and check during the next pass through the loop to see if the asynchronous operation has completed.
每循环一次检查下这个异步方法有没有完成。
// call the delegate asynchronously IAsyncResult result = flusher.BeginInvoke("data.dat", null, null); // wait for the call to finish result.AsyncWaitHandle.WaitOne(); // get the return value int returnValue = flusher.EndInvoke(result);
The final method is to create an instance of the
AsyncCallbackdelegate and pass it to the
BeginInvokemethod. The
AsyncCallbackdelegate is defined in the
Systemnamespace. It accepts an
IAsyncResultreference as its only parameter and returns void.
最后一个方法就是创建一个AsyncCallback委托的实例,然后把它传送给BeginInvoke方法。
When you pass an
AsyncCallbackto
BeginInvoke, the delegate will invoke that callback upon completion of the asynchronous operation. Then, you can use that opportunity to call
EndInvoketo retrieve the return value and perform any resource cleanup, as the next example shows.
args) { // create the object DataCache cache = new DataCache()] If you run this example you will see that the line. This is because the“Press Enter to Exit”will be displayed in the console window, followed by the output from [code]CallbackMethod
Mainmethod calls
BeginInvokeand continues processing, displaying its own output without waiting for the
CacheFlusherdelegate to finish. Later, when the asynchronous operation is complete, the delegate calls the
CallbackMethod, which writes its output to the screen.
The
Mainmethod begins by calling the delegate object’s
BeginInvokemethod as before. But, this time those second and third arguments are used. The second argument is an instance of another delegate named
AsynchCallback. The
AsynchCallbackdelegate is defined to accept an
IAsyncResultreference and return
void, such as the static
CallbackMethodprovided as part of the example. When the asynchronous operation is complete, the delegate invokes that callback. It is within the body of that
CallbackMethod, the method referenced by the
AsynchCallback, that we have the opportunity to call
EndInvoke.
Whatever object you pass to the third argument of
BeginInvokewill be stored in the
AsyncStateproperty of the
IAsyncResultreference passed to the
CallbackMethod. Notice in the
CallbackMethodthat the first thing we do is cast the
AsyncStateproperty to the CacheFlusher delegate. This is so that we can call the object’s
EndInvokemethod to receive the return value.
Events
One of the most common uses of delegates in .NET is event handling. Events are useful for notifying objects of user interface events or state changes. The following example creates aTimerobject that will fire an event every second. The
Timerclass is defined in the
System.Timersnamespace.
class DelegateDemo {
static void Main(string[] args) {
Timer t = new Timer(1000);
t.Elapsed +=
new ElapsedEventHandler(Timer_Elapsed);
t.Enabled = true;
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
static void Timer_Elapsed(object sender, ElapsedEventArgs e) {
Console.WriteLine("tick");
}
}
[/code]
The
Timerclass contains the
Elapsedevent and fires it whenever its interval expires. In this example the
Mainmethod instantiates a
Timerand registers an
ElapsedEventHandlerdelegate with its
Elapsedevent.
In this example, the method invoked by the
ElapsedEventHandlerdelegate is the
Timer_Elapsedmethod. Following the convention of all event handling delegates, the
ElapsedEventHandlerdelegate returns
voidand accepts two parameters. The first is a reference to the object that signaled the event and the second is a argument derived of
EventArgswhich stores pertinent information about the event.
Win32 callbacks
The last use for delegates that we will cover is to provide a way for unmanaged Windows API functions to call back into your managed code. Some Windows API functions accept the address of an application-defined function as a parameter and then call that function to perform some application-specific processing. An example is theEnumChildWindowsAPI function which enumerates all children of a parent window and invokes the application-defined callback function on each. To pass a function address from a C# program, we use a delegate.
The next example uses the
EnumChildWindowsAPI function to iterate through each of the top level windows on a system. Each window handle is passed to our application-defined callback method,
WindowEnumProc, which prints the windows text to the console.
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace enumchildren {
class WindowEnumerator {
// declare the delegate
public delegate bool WindowEnumDelegate (IntPtr hwnd,
int lParam);
// declare the API function to enumerate child windows
[DllImport("user32.dll")]
public static extern int EnumChildWindows(IntPtr hwnd,
WindowEnumDelegate del,
int lParam);
// declare the GetWindowText API function
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hwnd,
StringBuilder bld, int size);
static void Main(string[] args) {
// instantiate the delegate
WindowEnumDelegate del
= new WindowEnumDelegate(WindowEnumProc);
// call the win32 function
EnumChildWindows(IntPtr.Zero, del, 0);
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
public static bool WindowEnumProc(IntPtr hwnd,int lParam) {
// get the text from the window
StringBuilder bld = new StringBuilder(256);
GetWindowText(hwnd, bld, 256);
string text = bld.ToString();
if(text.Length > 0) {
Console.WriteLine(text);
}
return true;
}
}
}
[/code]
This example defines the
WindowEnumeratorclass which begins be defining a delegate named
WindowEnumDelegate. I have declared this delegate to have a compatible signature with the type of callback function that the
EnumChildWindowsfunction is expecting. Next I import two Windows API functions from the user32.dll.
The
Mainmethod only does three things. It first instantiates the
WindowEnumDelegateto call the
WindowEnumProcmethod defined in the listing. Then, it calls the
EnumChildWindowsAPI function, passing the delegate as argument. For each window whose parent is the value
NULLfrom C++ (which is
IntPtr.Zerofrom C#), the system will invoke my delegate. Each time my
WindowEnumProcis called, it receives the window handle of the current window as its first argument. It then calls
GetWindowTextto read the window’s title bar text into a buffer and display it to the console.
Conclusion
Delegates are used to create a reference to a method of another class. But, they are safer and far more versatile than C++ function pointers. Delegates are type-safe wrappers for methods. They are secure and verifiable. There are many applications for delegates. In this article, we demonstrated multicast delegates, threads, anonymous methods, events, and Win32 callbacks.相关文章推荐
- C# Delegates And Events in Depth[翻译加笔记]【Delegate】【1】
- C# Delegates And Events in Depth[翻译加笔记]【Event】【2】
- C# OpenCV学习笔记七之图片相似比较
- C# 中的委托和事件(http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx)
- C#学习笔记之——学生信息输入系统(Dictionary)
- 【C#学习笔记】获得系统时间
- C# in depth: Delegates and Events[读书笔记加翻译]
- C#学习笔记之比较、转换(11.2/3)
- Delegates and Events in C# / .NET
- nginx 源码学习笔记(二十一)—— event 模块(二) ——事件驱动核心ngx_process_events_and_timers
- C# 2012 step by step 学习笔记8 CHAPTER 9 Creating Value types with enumerations and Structures
- c#经典入门学习笔记-结构类型与对象的比较
- Delegates and Events in C# / .NET
- nginx 源码学习笔记(二十一)—— event 模块(二) ——事件驱动核心ngx_process_events_and_timers
- .NET深入学习笔记(2):C#中判断空字符串的4种方法性能比较与分析
- mo+c#(vs2005)开发gis系统学习笔记
- nginx 源码学习笔记(二十一)—— event 模块(二) ——事件驱动核心ngx_process_events_and_timers
- .NET深入学习笔记(2):C#中判断空字符串的4种方法性能比较与分析
- [C#] 类型学习笔记二:详解对象之间的比较
- Delegates and Events in C# .NET