C# .NET 中委托及其在事件中的作用
一个简单的委托示例。
引言
很多时候,我们会遇到一种情况,即一个对象需要在不同的状态下执行不同的操作。更有趣的是,我们发现对象本身并不知道这个需求。如果我们希望一个方法在不同时间执行不同的代码,会发生什么?这可以通过委托来处理。委托是一个类型安全函数指针。它允许附加不同的方法体进行执行。
声明委托
public delegate void OperationDelegate();
上面的委托不接受任何参数也不返回任何参数。如果你仔细观察,委托的签名与任何方法的签名都相似。它只需要使用 delegate
关键字作为前缀。
实例化委托
OperationDelegate operate = new OperationDelegate(MathOperations.Add);
这与实例化一个类不是完全一样吗?它在实例化时接受一个方法作为输入参数(MathOperations.Add
)。实例化委托的另一种方式是
OperationDelegate operation = MathOperations.Add;
哇,这就像赋值!!请参阅 SimpleDelegateExample.csproj,它演示了一个简单的委托声明和用法。addButton_click
和 subtractButton_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.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。
感谢您阅读本文。希望它对您有所帮助。欢迎任何建议/反馈。