我一直想知道代表们如何有用,我们为什么要使用它们?除了类型安全以及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 ... {}”。您是否还有其他示例来证明代表的有用性。
谢谢!
答案 0 :(得分:5)
代表是将功能注入方法的好方法。由于这个原因,他们非常有助于代码重用。
考虑一下,假设你有一组相关的方法具有几乎相同的功能,但仅仅在几行代码上有所不同。您可以将这些方法共有的所有内容重构为一个方法,然后您可以通过委托注入专门的功能。
以LINQ使用的所有IEnumerable扩展方法为例。所有这些都定义了常用功能,但需要传递给它们的委托来定义如何投影返回数据,或者如何过滤,排序等数据......
答案 1 :(得分:5)
我在C#中可以想到的最常见的代表日常使用是事件处理。当你在WinForm上有一个按钮,并且想要在单击按钮时执行某些操作时,你所做的就是在点击按钮时最终注册一个委托函数来被按钮调用。
所有这些都是在Visual Studio本身生成的代码中自动发生的,因此您可能看不到它发生的位置。
对您来说可能更有用的真实案例是,如果您想创建一个人们可以使用的库,该库将从Internet Feed中读取数据,并在Feed更新后通知他们。通过使用委托,使用您的库的程序员可以在更新订阅源时调用自己的代码。
答案 2 :(得分:4)
Lambda表达
代表们大多与事件一起使用。但动态语言显示出更广泛的用途。这就是为什么当我们得到Lambda expressions时,代表们在C#3.0之前没有得到充分利用。使用Lambda表达式(生成委托方法)很容易做某事
现在想象你有一个IEnumerable字符串。您可以轻松定义委托(使用Lambda表达式或任何其他方式)并将其应用于在每个元素上运行(例如修剪多余的空格)。并且不使用循环语句就这样做。当然,你的代表可能会做更复杂的任务。
答案 3 :(得分:3)
我将尝试列出一些超出简单if-else场景的示例:
实施回拨。例如,您正在解析XML文档,并希望在遇到特定节点时调用特定函数。您可以将委托传递给函数。
实施策略设计模式。将委托分配给所需的算法/策略实现。
在您希望在单独的线程上执行某些功能的情况下的匿名代理(此函数没有任何内容可以发送回主程序)。
其他人建议的活动订阅。
答案 4 :(得分:3)
代表只是.Net的first class functions实现,并允许使用它们的语言提供Higher Order Functions。
这种风格的主要好处是可以将常见方面抽象为一个功能,该功能完成它需要做的事情(例如遍历数据结构)并提供另一个功能(或函数),它要求它随着时间的推移做一些事情。
规范的功能示例是map和fold,可以通过提供其他操作来更改以执行各种操作。
如果你想要对T的列表求和并且有一些函数add需要两个T并将它们加在一起然后(通过部分应用)fold add 0
变为总和。 fold multiply 1
将成为产品,fold max 0
最大值。在所有这些示例中,程序员无需考虑如何迭代输入数据,无需担心输入为空时该怎么做。
这些是简单的例子(虽然它们与其他人结合时可能会出奇的强大)但考虑树遍历(一个更复杂的任务)所有这些都可以在treefold
函数后面抽象出来。编写树折函数可能很难,但一旦完成它就可以被广泛使用而不必担心错误。
这在概念和设计上类似于将foreach循环结构添加到传统的命令式语言中,这个想法是你不必自己编写循环控件(因为它引入了一个错误的机会,增加了详细程度妨碍了您对每个条目的操作,而不是显示如何获取每个条目。更高阶函数只允许您将结构的遍历与遍历时的操作分开<在语言本身内强大>可扩展。
应该注意的是,c#中的委托已经基本上被lambdas取代了,因为如果需要,编译器可以简单地将其视为一个不那么详细的委托,但也可以自由地通过表达式 lambda表示传递给它的函数,以允许(通常很复杂)重构或重新定位 desire 到其他域,例如通过Linq-to-Sql进行数据库查询。
.net委托模型对c样式函数指针的一个主要好处是它们实际上是要调用的函数的元组(两个数据)以及要在其上调用函数的可选对象。这允许您传递具有状态的函数,这更强大。由于编译器可以使用它来构造后面的类(1),因此实例化该类的新实例并将局部变量放入其中,从而允许closures。
(1)它 总是这样做,但是现在这是一个实现细节
答案 5 :(得分:2)
在你的例子中,你的恭维是一样的,所以你真正需要的是字符串数组。
如果您想在Command模式中使用委托,请想象您有:
public static void ShakeHands()
{ ... }
public static void HowAreYou()
{ ... }
public static void FrenchKissing()
{ ... }
您可以使用相同的签名替换方法,但可以使用不同的操作。 你选择了一个太简单的例子,我的建议是 - 去深度找一本书C#。
答案 6 :(得分:2)
这是一个真实世界的例子。我经常在包装某种外部调用时使用委托。例如,我们有一个旧的应用服务器(我希望它会消失),我们通过.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);
});
显然,错误处理比这更好,但你得到了粗略的想法。
答案 7 :(得分:1)
委托用于“调用”其他类中的代码(可能不一定是在同一个,类,或.cs中,甚至是同一个程序集)。
在您的示例中,代理可以简单地替换为您指出的if语句。
然而,代表指向代码中某处“生活”的函数的指针,这些函数出于组织原因(例如,您无法轻松访问)。
答案 8 :(得分:1)
代表和相关的语法糖已经显着改变了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
答案 9 :(得分:0)
我觉得很有用的另一个用途是,如果我希望执行相同的操作,传递相同的数据或在同一对象类型的多个实例中触发相同的操作。
答案 10 :(得分:0)
在.NET中,从后台线程更新UI时也需要委托。由于您无法从与创建控件的线程不同的线程更新控件,因此需要使用创建线程的上下文调用更新代码(主要使用this.Invoke)。