[英]Uses of delegates in C# (or other languages)
我一直想知道代表们如何有用,我们为什么要使用它们? 除了类型安全和Visual Studio文档中的所有这些优点之外,代表的真实用途是什么。
我已经找到了一个,它非常有针对性。
using System;
namespace HelloNamespace {
class Greetings{
public static void DisplayEnglish() {
Console.WriteLine("Hello, world!");
}
public static void DisplayItalian() {
Console.WriteLine("Ciao, mondo!");
}
public static void DisplaySpanish() {
Console.WriteLine("Hola, imundo!");
}
}
delegate void delGreeting();
class HelloWorld {
static void Main(string [] args) {
int iChoice=int.Parse(args[0]);
delGreeting [] arrayofGreetings={
new delGreeting(Greetings.DisplayEnglish),
new delGreeting(Greetings.DisplayItalian),
new delGreeting(Greetings.DisplaySpanish)};
arrayofGreetings[iChoice-1]();
}
}
}
但这并没有完全显示使用委托的优势,而不是一个条件“If ... {}”来解析参数并运行方法。
有谁知道为什么在这里使用委托更好,而不是“if ... {}”。 您是否还有其他示例来证明代表的有用性。
谢谢!
代理是将功能注入方法的好方法。 因此,它们极大地帮助了代码重用 。
考虑一下,假设你有一组相关的方法具有几乎相同的功能,但只是几行代码。 您可以将这些方法共有的所有内容重构为一个方法,然后您可以通过委托注入专用功能。
以LINQ使用的所有IEnumerable扩展方法为例。 所有这些都定义了常用功能,但需要传递给它们的委托来定义如何投影返回数据,或者如何过滤,排序等数据......
在C#中我能想到的最常见的代表日常使用是事件处理。 如果在WinForm上有一个按钮,并且想要在单击按钮时执行某些操作,那么您所做的就是在按下按钮时最终注册一个委托函数。
所有这些都是在Visual Studio自身生成的代码中自动发生的,因此您可能看不到它发生的位置。
对您来说可能更有用的真实案例是,如果您想创建一个人们可以使用的库,它将从Internet Feed中读取数据,并在更新Feed时通知他们。 通过使用委托,使用您的库的程序员可以在更新订阅源时调用自己的代码。
Lambda表达式
代表们大多与事件一起使用。 但动态语言显示出更广泛的用途。 这就是为什么当我们得到Lambda表达式时,代表们在C#3.0之前没有得到充分利用。 使用Lambda表达式(生成委托方法)很容易做某事
现在假设你有一个IEnumerable字符串。 您可以轻松定义委托(使用Lambda表达式或任何其他方式)并将其应用于在每个元素上运行(例如修剪多余的空格)。 并且不使用循环语句就这样做。 当然,你的代表可能会做更复杂的任务。
我将尝试列出一些超出简单的if-else场景的示例:
实施回电。 例如,您正在解析XML文档,并希望在遇到特定节点时调用特定函数。 您可以将委托传递给函数。
实施战略设计模式。 将委托分配给所需的算法/策略实现。
如果您希望在单独的线程上执行某些功能,则匿名委托(此函数没有任何内容可以发送回主程序)。
其他人建议的活动订阅。
代表只是.Net的第一类函数的实现,并允许使用它们的语言提供高阶函数 。
这种风格的主要好处是可以将公共方面抽象为一个函数,该函数完成它需要做的事情(例如遍历数据结构)并提供另一个函数(或函数),它要求做某事随着它的进展。
规范的功能示例是map和fold ,可以通过提供一些其他操作来改变以执行各种事情。
如果你想要对T的列表求和并且有一些函数add需要两个T并将它们加在一起然后(通过部分应用) fold add 0
变为sum。 fold multiply 1
将成为产品, fold max 0
为最大值。 在所有这些示例中,程序员无需考虑如何迭代输入数据,无需担心输入为空时该怎么做。
这些都是简单的例子(虽然它们与其他组合时可能会出奇的强大),但考虑树遍历(一个更复杂的任务),所有这些都可以在treefold
函数后面抽象出来。 编写树折功能可能很难,但一旦完成,它可以被广泛使用而不必担心错误。
这在概念和设计上类似于将foreach循环结构添加到传统的命令式语言,这个想法是你不必自己编写循环控件(因为它引入了一个错误的机会,增加了详细程度以你对每个条目所做的方式而不是显示你如何获得每个条目。高级函数只允许你将结构的遍历与在语言本身内可扩展遍历时的操作分开。
应该注意的是,c#中的委托已经基本上被lambdas取代了,因为如果需要,编译器可以简单地将其视为一个不那么详细的委托,但是也可以自由地将lambda表示的表达式传递给它传递给的函数以允许它(通常很复杂)通过Linq-to-Sql重构或重新定位欲望到其他领域,如数据库查询。
.net委托模型相对于c样式函数指针的一个主要好处是它们实际上是要调用的函数的元组(两个数据)以及要在其上调用函数的可选对象。 这允许您传递具有更强大状态的函数。 由于编译器可以使用它来构造后面的类(1),因此实例化该类的新实例并将局部变量放入其中,从而允许闭包 。
(1)它不必总是这样做,但现在这是一个实现细节
在你的例子中,你的恭维是一样的,所以你真正需要的是字符串数组。
如果您想在Command模式中使用委托,请想象您有:
public static void ShakeHands()
{ ... }
public static void HowAreYou()
{ ... }
public static void FrenchKissing()
{ ... }
您可以使用相同的签名替换方法,但可以使用不同的操作。 你选择了太简单的例子,我的建议是 - 去深度找一本C#书。
这是一个真实世界的例子。 我经常在包装某种外部调用时使用委托。 例如,我们有一个旧的应用服务器(我希望它会消失),我们通过.Net远程连接。 我将通过'safecall'函数在代理中调用app服务器,如下所示:
private delegate T AppServerDelegate<T>();
private T processAppServerRequest<T>(AppServerDelegate<T> delegate_) {
try{
return delegate_();
}
catch{
//Do a bunch of standard error handling here which will be
//the same for all appserver calls.
}
}
//Wrapped public call to AppServer
public int PostXYZRequest(string requestData1, string requestData2,
int pid, DateTime latestRequestTime){
processAppServerRequest<int>(
delegate {
return _appSvr.PostXYZRequest(
requestData1,
requestData2,
pid,
latestRequestTime);
});
显然,错误处理比这更好,但你得到了粗略的想法。
委托用于“调用”其他类中的代码(可能不一定在同一个,类,或.cs或甚至相同的程序集中)。
在您的示例中,代理可以简单地替换为您指出的if语句。
但是,代理指向代码中某处“生活”的函数,这些函数出于组织原因(例如,您无法访问)(轻松)。
代理和相关的语法糖已经显着改变了C#世界(2.0+)代表是类型安全的函数指针 - 因此您可以在未来的任何地方使用委托来调用/执行代码块 。
我能想到的广泛的部分
回调/事件处理程序 :在EventX发生时执行此操作。 或者在准备好我的异步方法调用的结果时执行此操作。
myButton.Click += delegate { Console.WriteLine("Robbery in progress. Call the cops!"); }
LINQ :元素的选择,投影等,您希望在将每个元素传递到管道之前对其执行某些操作。 例如,选择所有偶数的数字,然后返回每个数字的平方
var list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
.Where(delegate(int x) { return ((x % 2) == 0); })
.Select(delegate(int x) { return x * x; });
// results in 4, 16, 36, 64, 100
我觉得很有用的另一个用途是,如果我希望执行相同的操作,传递相同的数据或在同一对象类型的多个实例中触发相同的操作。
在.NET中,从后台线程更新UI时也需要委托。 由于您无法从与创建控件的线程不同的线程更新控件,因此您需要使用创建线程的上下文调用更新代码(主要使用this.Invoke)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.