C#
委托
什么是委托
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法。
委托用于将方法作为参数传递给其他方法。 事件处理程序就是通过委托调用的方法。
public delegate int PerformCalculation(int x, int y);
可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托。 该方法可以是静态方法,也可以是实例方法。 这样便能通过编程方式来更改方法调用,还可以向现有类中插入新代码。
备注:
在方法重载的上下文中,方法的签名不包括返回值。 但在委托的上下文中,签名包括返回值。 换句话说,方法和委托必须具有相同的返回类型。
将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。 例如,对比较两个对象的方法的引用可以作为参数传递到排序算法中。
委托概述
委托具有以下属性:
- 委托类似于 C++ 函数指针,但委托完全面向对象,不像 C++ 指针会记住函数,委托会同时封装对象实例和方法。
- 委托允许将方法作为参数进行传递。
- 委托可用于定义回调方法。
- 委托可以链接在一起;例如,可以对一个事件调用多个方法。
- 方法不必与委托类型完全匹配。 有关详细信息,请参阅使用委托中的变体。
- C# 2.0 版引入了匿名方法的概念,可以将代码块作为参数(而不是单独定义的方法)进行传递。 C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。 匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。 这些功能现在统称为匿名函数。
声明,实例化和使用委托
在C#1.0和更高的版本中,可以如下使用委托:
//Declare a delegate
delegate void Del(string str);
//Declare a method with the same signature as the delagate
static voi Notify(string name)
{
Console.WriteLine("Notification received for {0}",name);
}
//Create an instance of the delegate
Del del= new Del(Notify);
//Invoke the delegate
del("HelloWorld");
C# 2.0 提供了更简单的方法来编写前面的声明.
// C# 2.0 provides a simpler way to declare an instance of Del.
Del del2 = Notify;
在 C# 2.0 和更高版本中,还可以使用匿名方法来声明和初始化委托
// Instantiate Del by using an anonymous method.
Del del3 = delegate(string name)
{ Console.WriteLine("Notification received for: {0}", name); };
在 C# 3.0 和更高版本中,还可以通过使用 lambda 表达式声明和实例化委托
// Instantiate Del by using a lambda expression.
Del del4 = name => { Console.WriteLine("Notification received for: {0}", name); };
为委托增加或减少方法
委托其实是不变的,不过C#提供了看上去可以为委托添加或减少方法的语法,即使用+=
和-=
运算符。
Del del= Notify;
//增加方法
del+=delegate(string name)
{
//....
}
del+=name=>{Console.writeLine("Notification")};
//减少方法
del-=Notify;
事件
发布基于EventHandler模式的标准事件
-
将自定义数据的类声明为发布者和订阅者均可见的范围
public class CustomEventArgs : EventArgs { public CustomEventArgs(string s) { msg = s; } private string msg; public string Message { get { return msg; } } }
-
委托类型的声明: 事件和事件处理程序必须有共同的签名和返回值,他们通过委托描述
public delegate void CustomEventHandler(object sender, CustomEventArgs e);
- .NET Framework 2.0 引入了泛型版本的委托 EventHandler<TEventArgs>, 则可以跳过这步,直接使用
EventHandler<CustomEventArgs>
. -
sender
用来保存触发事件的对象的引用。 -
e
用来保存状态信息,EventArgs
被设计不可传递数据,如果希望传递数据,可以派生自EventArgs
的类。
- .NET Framework 2.0 引入了泛型版本的委托 EventHandler<TEventArgs>, 则可以跳过这步,直接使用
-
事件处理程序声明 : 订阅者类中会在事件触发时执行的方法。
void HandleCustomEvent(object sender, CustomEventArgs e) { Console.WriteLine(id + " received this message: {0}", e.Message); }
-
事件声明:发布者类必须声明一个订阅类可以注册的事件成员。
-
如果没有使用自定义的
EventArgs
类,事件类型为非泛型EventHandler
委托。public event EventHandler RaiseCustomEvent;
-
如果使用非泛型
EventHandler
并派生自EventArgs
的自定义的类。public event CustomEventHandler RaiseCustomEvent;
-
如果使用泛型版本,则无需自定义委托,而在发布类中,将事件类型指定为
EventHandler<CustomEventArgs>
.public event EventHandler<CustomEventArgs> RaiseCustomEvent;
-
-
事件注册:订阅者必须订阅事件才能在他被触发时得到通知。
pub.RaiseCustomEvent += HandleCustomEvent;
-
触发事件:发布者类中”触发”事件。
protected virtual void OnRaiseCustomEvent(CustomEventArgs e) { EventHandler<CustomEventArgs> handler = RaiseCustomEvent; // Event will be null if there are no subscribers if (handler != null) { // Format the string to send inside the CustomEventArgs parameter e.Message += $" at {DateTime.Now}"; // Use the () operator to raise the event. handler(this, e); } }
示例
下例通过使用自定义 EventArgs
类和 EventHandler 作为事件类型来演示之前的步骤。
namespace DotNetEvents
{
using System;
using System.Collections.Generic;
// Define a class to hold custom event info
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string s)
{
message = s;
}
private string message;
public string Message
{
get { return message; }
set { message = value; }
}
}
// Class that publishes an event
class Publisher
{
// Declare the event using EventHandler<T>
public event EventHandler<CustomEventArgs> RaiseCustomEvent;
public void DoSomething()
{
// Write some code that does something useful here
// then raise the event. You can also raise an event
// before you execute a block of code.
OnRaiseCustomEvent(new CustomEventArgs("Did something"));
}
// Wrap event invocations inside a protected virtual method
// to allow derived classes to override the event invocation behavior
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
// Event will be null if there are no subscribers
if (handler != null)
{
// Format the string to send inside the CustomEventArgs parameter
e.Message += $" at {DateTime.Now}";
// Use the () operator to raise the event.
handler(this, e);
}
}
}
//Class that subscribes to an event
class Subscriber
{
private string id;
public Subscriber(string ID, Publisher pub)
{
id = ID;
// Subscribe to the event using C# 2.0 syntax
pub.RaiseCustomEvent += HandleCustomEvent;
}
// Define what actions to take when the event is raised.
void HandleCustomEvent(object sender, CustomEventArgs e)
{
Console.WriteLine(id + " received this message: {0}", e.Message);
}
}
class Program
{
static void Main(string[] args)
{
Publisher pub = new Publisher();
Subscriber sub1 = new Subscriber("sub1", pub);
Subscriber sub2 = new Subscriber("sub2", pub);
// Call the method that raises the event.
pub.DoSomething();
// Keep the console window open
Console.WriteLine("Press Enter to close this window.");
Console.ReadLine();
}
}
}