65.9K
CodeProject 正在变化。 阅读更多。
Home

C# .NET 中委托及其在事件中的作用

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.95/5 (11投票s)

2009 年 5 月 13 日

CPOL

3分钟阅读

viewsIcon

42328

downloadIcon

593

一个简单的委托示例。

引言

很多时候,我们会遇到一种情况,即一个对象需要在不同的状态下执行不同的操作。更有趣的是,我们发现对象本身并不知道这个需求。如果我们希望一个方法在不同时间执行不同的代码,会发生什么?这可以通过委托来处理。委托是一个类型安全函数指针。它允许附加不同的方法体进行执行。

声明委托

public delegate void OperationDelegate();

上面的委托不接受任何参数也不返回任何参数。如果你仔细观察,委托的签名与任何方法的签名都相似。它只需要使用 delegate 关键字作为前缀。

实例化委托

OperationDelegate operate = new OperationDelegate(MathOperations.Add);

这与实例化一个类不是完全一样吗?它在实例化时接受一个方法作为输入参数(MathOperations.Add)。实例化委托的另一种方式是

OperationDelegate operation = MathOperations.Add;

哇,这就像赋值!!请参阅 SimpleDelegateExample.csproj,它演示了一个简单的委托声明和用法。addButton_clicksubtractButton_Click 代码片段演示了使用委托的不同方法。您可以通过注释掉其他方法来检查每种方法。 [每种方法都用块开始块结束包围]。

private void addButton_Click(object sender, EventArgs e)
{
    //Code Usage:
    //Use one block and comment other blocks.
    //Block1 explains simple use of delegate
    //It creates a local varaible of OpeartionDelegate and assign it
    //Subtract method of MathOperations
    //Block2 explains passing delegate as parameter to a method
    //It calls DoOperation method which executes the input delegate
    //Block3 explains use of anonymous delegate
    //This is a backbone for LINQ lambda functors

    //Block1 starts
    int value1, value2;
    int.TryParse(firstValueTextBox.Text, out value1);
    int.TryParse(secondValueTextBox.Text, out value2);
    OperationDelegate operation = MathOperations.Add;
    //Add method is assigned to operation delegate
    resultTextBox.Text = operation(value1, value2).ToString();
    //Block1 ends

    //Block2 starts
    DoOperation(MathOperations.Add);
    //Add method is passed as method parameter. 
    //This is possible because Add method signature is int 
    //XYZ(int,int) same as OperationDelegate
    //Block2 ends

    //Block3 starts
    DoOperation(delegate(int v1, int v2) { return v1 + v2; });
    //here instead of passing some method an anonymous delegate is
    // passed as input parameter
    //Signature of anonymous delegate is same as OperationDelegate
    //Block3 ends
}

委托的匿名方法

如果我们想为委托定义自定义执行,但找不到任何与该签名匹配的方法,会发生什么?而且,我们只想写一两行代码。那为什么还要写一个方法呢?我们可以定义匿名方法。匿名方法类似于内联方法。

delegate(int v1, int v2) { return v1 - v2; }

上面的匿名方法接受两个 int 作为输入并返回减去的值。

多播委托

多播委托是指允许用户附加多个方法的委托。它通常应该返回 void。但这并非绝对。我们可以将多个方法附加到返回值的委托。在这种情况下,将存储最后执行方法的返回值。例如,如果您定义一个返回 int 的委托并向其附加多个方法,则委托的返回值将是最后一个执行的方法的返回值。因此,开发人员应避免这样做。[注意:对于将多个方法附加到委托的执行顺序,.NET Framework 不提供保证。] .NET Framework 提供了 EventHandler 作为多播委托。EventHandler 的签名如下:

public delegate void EventHandler(object sender, EventArgs e)j

自定义多播委托

//Declare a delegate returning void
public delegate void OperationDelegate();
//instance declaration
OperationDelegate Operate;
//attach a method SomeMethodReturningVoid
Operate += SomeMethodReturningVoid;
//attach another method
Operate += delegate() { MessageBox.Show("Multicast example"); };

委托的更多特化

在某些情况下,附加的方法不必与委托签名完全匹配。更具体地说,当委托的定义签名接受基类或返回基类时,它们与签名不匹配。这是继承的魔力,它允许附加的方法签名与委托的签名不匹配。让我们看看如何

协变委托

当委托签名返回基变量时,它被称为协变委托。

internal delegate BaseClass CovarianceDelegate(string name);
//delegate which returns base class object is Covariance delegate

//create instance of delegate (Note: DelegateUtility.GetInstance method)
//returns derived class instance depend on input parameters)
CovarianceDelegate covarinceDelegateInstance = 
  new CovarianceDelegate(DelegateUtility.GetInstance);

//execute delegate instance returning base class instance
BaseClass instance = covarinceDelegateInstance("DerivedClass1");

//execute work method of instance
instance.Work();

逆变委托

当委托接受基类类型的变量时,它被称为逆变委托。

internal delegate void ContravarianceDelegate(BaseClass instance);
//delegate which accepts base class object 
//as input parameter is Contravariance delegate. 

ContravarianceDelegate contravarianceDelegateInstance = 
   new ContravarianceDelegate(DelegateUtility.DoWork);

//execute contravariance delegate by passing it derived class instance.
contravarianceDelegateInstance(new DerivedClass2());

委托的问题

现在考虑一个场景。我们有一个类(classA),它包含变量和一个委托。两个不同的类持有对 classA 的引用。每个类都将其自己的方法附加到委托。在执行阶段,某个类遇到一种情况,它需要将 classA 委托设置为 null。由于委托是一个函数指针,我们可以将其设置为 null。这样做会导致系统陷入混乱。您想知道为什么吗?其他类如何知道委托现在被设置为 null?它会清除附加到委托的其他类的方法。

DelegateIssue.jpg

这个场景在 DelegateIssue.csproj 中进行了描述。

.NET 中的事件

我们可以通过事件通知来避免上述情况。.NET 使用发布者-订阅者机制来通知事件。.NET 事件框架底层使用委托作为通信媒介(事件处理程序连接)。基于事件的设计的优点是,您(订阅者)无法将事件设置为 null。订阅者可以订阅/取消订阅事件,但他们不能将其设置为 null。只有发布者可以这样做。其次,除了事件发布者,任何人都无法引发事件。听起来很棒!!!

//Delegate defination
public delegate void OperationDelegate();
//event declaration as private variable and public property
private event OperationDelegate _operate;

public event OperationDelegate Operate 
{
  add { _operate += value; } 
  remove { _operate -= value; } }
  //wiring event using underneath delegate
  _data.Operate += this.Operation;

有关详细信息,请参阅 eventExample.csproj

感谢您阅读本文。希望它对您有所帮助。欢迎任何建议/反馈。

© . All rights reserved.