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

委托和事件的 6 个重要用法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (204投票s)

2010年6月2日

CPOL

10分钟阅读

viewsIcon

646478

downloadIcon

3225

委托和事件的 6 个重要用法

委托和事件的 6 个重要用法

引言
方法和函数的抽象问题
如何声明委托?
使用委托解决抽象指针问题
多播委托
多播委托的简单演示
多播委托的问题——暴露无遗
事件——委托的封装
实现事件
委托和事件的区别
用于异步方法调用的委托
总结委托的用途
委托和C++指针的区别
源代码
 

如果您不想阅读我完整的文章,可以观看以下有关C#中事件和委托的视频。

 

引言
 

在本文中,我们将首先尝试理解委托解决了什么问题,然后创建一个简单的委托并尝试解决该问题。接下来,我们将尝试理解多播委托的概念以及事件如何帮助封装委托。最后,我们将理解事件和委托之间的区别,并了解如何异步调用委托。


当我们完成所有基础知识后,将总结委托的六个重要用途。

这是一本为所有.NET朋友准备的小型电子书,涵盖了WCF、WPF、WWF、Ajax、Core .NET、SQL等主题,您可以从这里下载。

方法和函数的抽象问题
 

在我们继续讨论委托之前,让我们先尝试理解委托解决了什么问题。下面是一个名为“ClsMaths”的简单类,其中包含一个简单的“Add”函数。这个“ClsMaths”类由一个简单的UI客户端使用。现在,假设随着时间的推移,您在“ClsMaths”类中添加了减法功能,您的客户端也需要相应地进行更改以适应新功能。
换句话说,在类中添加新功能会导致UI客户端重新编译。
 

x`

简而言之,问题在于函数名与UI客户端之间存在紧密耦合。那么如何解决这个问题呢?与其引用UI/客户端中的实际方法,不如引用一个指向这些方法的抽象指针,这样我们就可以将函数与UI解耦。
之后,对“ClsMath”类的任何更改都不会影响UI,因为更改将由抽象指针解耦。这个抽象指针可以使用委托来定义。委托定义了指向函数/方法的简单抽象指针。
 

好的,既然我们已经理解了紧密耦合问题以及解决方案,让我们尝试理解如何定义一个简单的委托。
 

如何声明委托?
 

实现委托是一个四步过程:声明、创建、指向和调用。
 

第一步是声明具有相同返回类型和输入参数的委托。例如,上面的“add”函数有两个整数输入参数和一个整数输出参数。

private int Add(int i,int y)
{
return i + y;
}

 


因此,对于上面的方法,委托需要以下面的方式定义。请注意代码中附加的“delegate”关键字。
 

// Declare delegate
public delegate int PointetoAddFunction(int i,int y);

 

委托的返回类型和输入类型需要兼容,如果不兼容,将显示如下错误,如以下图片所示。
 

 

下一步(第二步)是创建委托引用。
 

// Create delegate reference
PointetoAddFunction myptr = null;

第三步是将委托引用指向方法,目前我们需要将委托引用指向add函数。
 

// Point the reference to the add method
myptr = this.Add;

 

最后,我们需要使用委托的“Invoke”函数来调用委托函数。
 

// Invoke the delegate
myptr.Invoke(20, 10)

下图总结了上述四个步骤如何映射到代码片段。
 

使用委托解决抽象指针问题
 

为了解耦算法更改,我们可以通过一个抽象委托公开所有以下算术函数。
 

因此,第一步是添加一个通用的委托,它产生一个输出并接受两个输入,如下面的代码片段所示。

public class clsMaths
{
public delegate int PointerMaths(int i, int y);
}

 

下一步是公开一个函数,该函数接受操作并将一个附加的委托公开给UI,如下面的代码片段所示。
 

public class clsMaths
{
public delegate int PointerMaths(int i, int y);

public PointerMaths getPointer(int intoperation)
{
PointerMaths objpointer = null;
if (intoperation == 1)
{
objpointer = Add;
}
else if (intoperation == 2)
{
objpointer = Sub;
}
else if (intoperation == 3)
{
objpointer = Multi;
}
else if (intoperation == 4)
{
objpointer = Div;
}
return objpointer;
}
}

 

下面是完整的代码片段。所有算法函数,即“Add”、“Sub”等,都被设为私有,只公开一个通用的抽象委托指针,可用于调用这些算法函数。
 

public class clsMaths
{
public delegate int PointerMaths(int i, int y);

public PointerMaths getPointer(int intoperation)
{
PointerMaths objpointer = null;
if (intoperation == 1)
{
objpointer = Add;
}
else if (intoperation == 2)
{
objpointer = Sub;
}
else if (intoperation == 3)
{
objpointer = Multi;
}
else if (intoperation == 4)
{
objpointer = Div;
}
return objpointer;
}

private int Add(int i, int y)
{
return i + y;
}
private int Sub(int i, int y)
{
return i - y;
}
private int Multi(int i, int y)
{
return i * y;
}
private int Div(int i, int y)
{
return i / y;
}
}

 

因此,在客户端,调用变得通用,没有任何与实际方法名称(如“Add”、“Sub”等)的耦合。
 

int intResult = objMath.getPointer(intOPeration).Invoke(intNumber1,intNumber2);

 

多播委托
 

在我们之前的例子中,我们看到了如何创建指向函数或方法的委托指针。我们还可以创建指向多个函数和方法的委托。如果我们调用这样的委托,它将依次调用与该委托相关的所有方法和函数。
下面的代码片段将两个方法,即method1和method2,附加到委托“delegateptr”。为了添加多个方法和函数,我们需要使用“+=”符号。现在,如果我们调用委托,它将首先调用“Method1”,然后调用“Method2”。调用发生的顺序与附加的顺序相同。
 

// Associate method1
delegateptr += Method1;
// Associate Method2
delegateptr += Method2;
// Invoke the Method1 and Method2 sequentially
delegateptr.Invoke();

 

那么我们如何在实际项目中 M使用多播委托呢?很多时候我们想创建发布者/订阅者模型。例如,在一个应用程序中,可以有各种错误日志记录例程,并且一旦应用程序发生错误,您就会希望将错误广播到相应的组件。
 

多播委托的简单演示
 

为了更好地理解多播委托,让我们做以下演示。在这个演示中,我们有“Form1”、“Form2”和“Form3”。“Form1”有一个多播委托,它将事件传播给“Form2”和“Form3”。
 

在“Form1”的窗体级别(此窗体将事件传播给form2和form3),我们将首先定义一个简单的委托以及该委托的引用,如下面的代码片段所示。此委托将负责向其他窗体广播事件。
 

// Create a simple delegate
public delegate void CallEveryOne();

// Create a reference to the delegate
public CallEveryOne ptrcall=null;
// Create objects of both forms

public Form2 obj= new Form2();
public Form3 obj1= new Form3();

 

在窗体加载时,我们以多播方式(+=)调用窗体并附加存在于两个窗体中的“CallMe”方法。
 

private void Form1_Load(object sender, EventArgs e)
{
// Show both the forms
obj.Show();
obj1.Show();
// Attach the form methods where you will make call back
ptrcall += obj.CallMe;
ptrcall += obj1.CallMe;
}

 

最后,我们可以调用并向两个窗体广播方法调用。
 

private void button1_Click(object sender, EventArgs e)
{
// Invoke the delegate
ptrcall.Invoke();
}

 

多播委托的问题——暴露无遗
 

上述代码的第一个问题是,订阅者(form2和form3)无权表明他们对事件感兴趣或不感兴趣。这一切都由“form1”决定。
我们可以走另一条路,即:将委托传递给订阅者,让他们在希望订阅“form1”发送的广播时附加他们的方法。但这会导致另一组问题,即封装违反。
如果我们向订阅者公开委托,他们可以调用委托,添加自己的函数等。换句话说,委托对订阅者来说是完全暴露的。
 

事件——委托的封装
 

事件有助于解决委托的封装问题。事件位于委托之上,提供封装,以便目标源只能收听而不能完全控制委托对象。
下图显示了事物的样子:
• 方法和函数通过委托进行抽象/封装
• 通过使用多播委托,委托得到了进一步扩展,以提供广播模型。
• 多播委托通过事件进一步封装。
 

实现事件
 

因此,让我们以使用多播委托的相同示例,并尝试使用事件来实现它。事件在内部使用委托,因为事件 over 委托提供了更高级别的封装。
因此,在发布者(“Form1”)中的第一步是定义委托和该委托的事件。下面的代码片段就是如此,请注意“event”关键字。
我们定义了一个委托“CallEveryOne”,并为该委托指定了一个名为“EventCallEveryOne”的事件对象。
 

public delegate void CallEveryone();
public event CallEveryone EventCallEveryOne;

 

从发布者,即“Form1”,创建“Form2”和“Form3”对象,并附加当前的“Form1”对象,以便“Form2”和“Form3”可以收听事件。对象附加后,引发事件。
 

Form2 obj = new Form2();
obj.obj = this;
Form3 obj1 = new Form3();
obj1.obj = this;
obj.Show();
obj1.Show();
EventCallEveryOne();

 

在订阅者端,即(Form2和Form3),将方法附加到事件监听器。
 

obj.EventCallEveryOne += Callme;

 

这段代码将显示与我们从多播委托示例中获得的结果相同的结果。
 

委托和事件的区别
 

那么,除了事件的糖衣语法之外,委托和事件之间到底有什么区别?如前所述,主要区别在于事件比委托提供了一个更高级别的封装。
因此,当我们传递委托时,它是裸露的,并且目标/订阅者可以修改委托。当我们使用事件时,目标只能收听它。
 

用于异步方法调用的委托
 

委托的另一个用途是异步方法调用。您可以异步调用委托指向的方法和函数。
异步调用意味着客户端调用委托,并且立即将控制权返回给进一步执行。委托与主调用者并行运行。当委托完成其工作时,它会调用调用者,告知函数/子例程已完成执行。
 

要异步调用委托,我们需要调用“begininvoke”方法。在“begininvoke”方法中,我们需要指定当前的回调方法“CallbackMethod”。
 

delegateptr.BeginInvoke(new AsyncCallback(CallbackMethod), delegateptr);

 

下面的代码片段是“CallbackMethod”。一旦委托完成其任务,就会调用此方法。
 

static void CallbackMethod(IAsyncResult result)
{
int returnValue = flusher.EndInvoke(result);
}

总结委托的用途
 

委托有 6 个重要用途:
1. 抽象和封装方法(匿名调用)
这是委托最重要的用途;它帮助我们定义一个可以指向方法和函数的抽象指针。同一个抽象委托稍后可以用来指向该类型的方法和函数。在上一节中,我们展示了一个简单的数学类示例。后来添加新的算法函数不会影响UI代码。

2. 回调机制很多时候我们希望提供一个回调机制。委托可以传递给目的地,目的地可以使用相同的委托指针进行回调。

3. 异步处理通过使用“BeginInvoke”和“EndInvoke”,我们可以异步调用委托。我们在上一节中详细解释了这一点。

4. 多播——顺序处理有时我们希望按顺序调用某些方法,这可以通过使用多播委托来完成。这已经在上面显示的多播示例中进行了说明。

5. 事件——发布者订阅模型我们可以使用事件来创建纯粹的发布者/订阅者模型。
 

委托和C++指针的区别

 

C++指针不是类型安全的,换句话说,它可以指向任何类型的方法。另一方面,委托是类型安全的。指向整数返回类型的委托不能指向字符串返回类型。

 

源代码

您可以从这里下载本教程的源代码。

进一步阅读,请观看下面的面试准备视频和分步视频系列。

© . All rights reserved.