代表们,为什么?

可能重复:
你什么时候在C#中使用委托?
代表的目的

我看到很多关于代表使用的问题。 我仍然不清楚在哪里,为什么你会使用委托,而不是直接调用方法。

我多次听到这句话:“委托对象可以被传递给可以调用引用方法的代码,而不必在编译时知道哪个方法会被调用。

我不明白这种说法是正确的。

我写了下面的例子。 假设你有3个相同参数的方法:

public int add(int x, int y) { int total; return total = x + y; } public int multiply(int x, int y) { int total; return total = x * y; } public int subtract(int x, int y) { int total; return total = x - y; } 

现在我宣布一个代表:

 public delegate int Operations(int x, int y); 

现在我可以进一步声明一个处理程序来使用这个委托(或你的委托直接)

呼叫代表:

 MyClass f = new MyClass(); Operations p = new Operations(f.multiply); p.Invoke(5, 5); 

或者用处理程序调用

 f.OperationsHandler = f.multiply; //just displaying result to text as an example textBoxDelegate.Text = f.OperationsHandler.Invoke(5, 5).ToString(); 

在这两种情况下,我都看到了我的“繁殖”方法。 为什么人们使用短语“在运行时更改function”或上面的那个?

为什么委托使用,如果每次我声明委托,它需要一个方法指向? 如果它需要一个方法指向,为什么不直接调用该方法? 在我看来,我不得不编写更多的代码来使用代表,而不是直接使用函数。

有人能给我一个真实的世界情况吗? 我完全困惑。

在运行时改变function不是代表完成的。

基本上,代表为您节省了一大笔钱。

例如:

 class Person { public string Name { get; } public int Age { get; } public double Height { get; } public double Weight { get; } } IEnumerable<Person> people = GetPeople(); var orderedByName = people.OrderBy(p => p.Name); var orderedByAge = people.OrderBy(p => p.Age); var orderedByHeight = people.OrderBy(p => p.Height); var orderedByWeight = people.OrderBy(p => p.Weight); 

在上面的代码中, p => p.Namep => p.Age等都是lambdaexpression式,其值分别为Func<Person, T> delegates(其中Tstringintdoubledouble ) )。

现在让我们考虑一下如何在没有代表的情况下实现上述目标。 OrderBy方法没有采用委托参数,我们不得不放弃通用性并定义这些方法:

 public static IEnumerable<Person> OrderByName(this IEnumerable<Person> people); public static IEnumerable<Person> OrderByAge(this IEnumerable<Person> people); public static IEnumerable<Person> OrderByHeight(this IEnumerable<Person> people); public static IEnumerable<Person> OrderByWeight(this IEnumerable<Person> people); 

这将完全吸引 。 我的意思是,首先,代码已经变得无限重用,因为它只适用于Persontypes的集合。 此外,我们需要复制和粘贴相同的代码四次,每个副本只更改1或2行(引用Person的相关属性,否则它们看起来都是一样的)! 这很快就会变成一个难以维系的烂摊子。

所以代表允许你通过抽象代码中可以切换的代码来使代码更加可重用更易于维护。

.NET代表:AC#睡前故事

代表们非常有用,特别是在引入linq和闭包之后。

一个很好的例子是“Where”函数,它是标准的linq方法之一。 “哪里”有一个列表和一个filter,并返回与filter匹配的项目列表。 (filter参数是一个代表T,并返回一个布尔值。)

因为它使用委托来指定filter,所以Where函数非常灵活。 例如,您不需要使用不同的Where函数来过滤奇数和素数。 调用语法也非常简洁,如果使用接口或抽象类,则不会出现这种情况。

更具体地说,如果拿一个代表意味着你可以这样写:

 var result = list.Where(x => x != null); ... 

而不是这个:

 var result = new List<T>(); foreach (var e in list) if (e != null) result.add(e) ... 

为什么委托使用,如果每次我声明一个委托,它需要一个方法指向? 如果它需要一个方法指向,为什么不直接调用该方法?

像接口一样,委托可以让你的代码解耦和概括。 当你事先不知道你想要执行什么方法时,你通常会使用委托 – 当你只知道你要执行一些与某个签名匹配的东西时

例如,考虑一个定时器类,它将定期执行一些方法:

 public delegate void SimpleAction(); public class Timer { public Timer(int secondsBetweenActions, SimpleAction simpleAction) {} } 

您可以将任何东西插入该计时器,以便您可以在任何其他项目或应用程序中使用它,而无需预测您将如何使用它,并且不会将其用途限制在您现在正在考虑的一小部分场景中。

让我举一个例子。 如果你的类暴露了一个event ,它可以在运行时分配一定数量的委托,这将被调用来表示发生了一些事情。 当你写了课,你不知道什么代表它会结束运行。 相反,这取决于谁使用你的class级。

举一个具体的例子,对我来说一个特别的最近使用的是System.Net.Mail.SmtpClient上的SendAsync() 。 我有一个应用程序发送吨和吨的电子邮件,并有一个明显的性能冲击,等待Exchange服务器接受消息。 但是,有必要logging与该服务器交互的结果。

所以我写了一个委托方法来处理这个日志logging,并在发送每封邮件时将它传递给SendAsync() (我们以前只是使用Send() )。 这样它可以callback委托来logging结果,并且应用程序线程不会等待交互完成才能继续。

如果您希望应用程序在不等待交互完成的情况下继续运行,则可能也是如此。 Web服务的代理类等利用了这一点。

一个需要delegate例子是当你必须在UI线程中修改一个控件,并且你正在使用不同的线程。 例如,

 public delegate void UpdateTextBox(string data); private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { ... Invoke(new UpdateTextBox(textBoxData), data); ... } private void textBoxData(string data) { textBox1.Text += data; } 

在你的例子中,一旦你将一个委托赋值给一个variables,你可以像其他variables一样传递它。 您可以创build一个接受委托作为参数的方法,并且可以调用该委托,而无需知道方法的真正声明位置。

 private int DoSomeOperation( Operations operation ) { return operation.Invoke(5,5); } ... MyClass f = new MyClass(); Operations p = new Operations(f.multiply); int result = DoSomeOperation( p ); 

代表使方法成为可以像int一样传递的东西。 你可以说variables不会给你任何额外的,因为在

 int i = 5; Console.Write( i + 10 ); 

你看到指定的值5,所以你可能只是说Console.Write( 5 + 10 ) 。 在这种情况下是对的,但是却错过了能说的好处

 DateTime nextWeek = DateTime.Now.AddDays(7); 

而不必定义一个特定的DateTime.AddSevenDays()方法和一个AddSixDays方法等等。

您可以使用委托实现订阅和事件处理程序。 你也可以(以一种可怕的方式)使用它们来解决循环依赖。

或者如果你有一个计算引擎,并且有很多可能的计算,那么你可以使用一个参数委托,而不是你的引擎的许多不同的函数调用。

使用您的Operations示例,想象一个有几个button的计算器。 你可以像这样为你的button创build一个类

 class CalcButton extends Button { Operations myOp; public CalcButton(Operations op) { this.myOp=op; } public void OnClick(Event e) { setA( this.myOp(getA(), getB()) ); // perform the operation } } 

然后当你创buildbutton,你可以创build每个不同的操作

 CalcButton addButton = new CalcButton(new Operations(f.multiply)); 

这有好几个原因。 你不会复制button中的代码,它们是通用的。 您可以有多个button,都具有相同的操作,例如在不同的面板或菜单上。 您可以随时更改与button关联的操作。

代表用于解决Access问题。 当你想有对象foo需要调用对象栏的frob方法,但不能访问frob方法。

对象goo可以同时访问foo和bar,因此可以使用委托将它们绑定在一起。 典型的酒吧和咕噜声通常是同一个对象。

例如一个Button类通常没有任何访问权限的类定义了一个Button_click方法。

所以现在我们已经可以将它用于除了事件以外的所有事情。 asynchronous模式和Linq就是两个例子。

似乎很多答案都与内联代表有关,在我看来,这比我所说的“经典代表”更容易理解。

下面是我的代表如何让代码允许消费类更改或增加行为(通过有效地添加“挂钩”,以便消费者可以在关键操作之前或之后执行操作和/或完全阻止该行为)。 请注意,所有的决策逻辑都是从StringSaver类的外部提供的。 现在考虑一下这个类可能有4个不同的消费者 – 他们每个人都可以实现自己的VerificationNotification逻辑,或者根据情况不用。

 internal class StringSaver { public void Save() { if(BeforeSave != null) { var shouldProceed = BeforeSave(thingsToSave); if(!shouldProceed) return; } BeforeSave(thingsToSave); // do the save if (AfterSave != null) AfterSave(); } IList<string> thingsToSave; public void Add(string thing) { thingsToSave.Add(thing); } public Verification BeforeSave; public Notification AfterSave; } public delegate bool Verification(IEnumerable<string> thingsBeingSaved); public delegate void Notification(); public class SomeUtility { public void SaveSomeStrings(params string[] strings) { var saver = new StringSaver { BeforeSave = ValidateStrings, AfterSave = ReportSuccess }; foreach (var s in strings) saver.Add(s); saver.Save(); } bool ValidateStrings(IEnumerable<string> strings) { return !strings.Any(s => s.Contains("RESTRICTED")); } void ReportSuccess() { Console.WriteLine("Saved successfully"); } } 

我想重点是委托指出的方法不一定暴露委托成员的类。