C#代表

我在理解C#中的委托如何工作方面遇到了一些麻烦。 我有很多代码示例,但我仍然无法正确掌握它。

有人能用“普通英语”向我解释一下吗? 当然! 代码的例子会有所帮助,但我想我需要更多地描述它是如何/为什么有效的。

编辑:

那么,问题是:为什么代表们工作? 什么是整个过程的“流程图”?

使用代表的先决条件是什么?

我希望这会使问题更清楚。

考虑委托的一种方法就像是对函数引用 。 例如,假设您在窗口中有一个按钮,并且您希望在单击按钮时发生某些事情。 您可以将委托附加到按钮的Click事件,每当用户单击此按钮时,您的函数将被执行。

class MyWindow : Window { Button _button; public MyWindow() { _button = new Button(); // place the button in the window _button.Click += MyWindow.ButtonClicked; } static void ButtonClicked(object sender, RoutedEventArgs e) { MessageBox.Show("Button Clicked"); } } 

请注意我如何使ButtonClicked成为一个静态函数 – 我想对接下来的非静态函数提出一个观点。 假设ButtonClicked是一个非静态成员:

 class MyWindow : Window { Button _button; int _numClicked = 0; public MyWindow() { this._button = new Button(); // place the button in the window this._button.Click += this.ButtonClicked; } void ButtonClicked(object sender, RoutedEventArgs e) { this._numClicked += 1; MessageBox.Show("Button Clicked " + this._numClicked + " times"); } } 

现在,委托包含对函数“ButtonClicked”的引用和实例,“this”,该方法被调用。 MyWindow构造函数中的实例“this”和ButtonClicked中的“this”是相同的。

这是一个称为闭包的概念的特定情况,它允许在创建委托时“保存”状态 – 当前对象,局部变量等。 在上面的例子中,我们使用委托中的构造函数中的“this”。 我们可以做的不仅仅是:

 class MyWindow : Window { Button _button; int _numClicked = 0; public MyWindow(string localStringParam) { string localStringVar = "a local variable"; this._button = new Button(); // place the button in the window this._button.Click += new RoutedEventHandler( delegate(object sender, RoutedEventArgs args) { this._numClicked += 1; MessageBox.Show("Param was: " + localStringParam + " and local var " + localStringVar + " button clicked " + this._numClicked + " times"); }); } } 

在这里,我们创建了一个匿名委托 – 一个没有明确名称的函数。 引用此函数的唯一方法是使用RoutedEventHandler委托对象。 此外,此函数存在于MyWindow构造函数的范围内,因此它可以访问所有本地参数,变量和成员实例“this”。 即使在MyWindow构造函数退出后,它仍将继续保持对局部变量和参数的引用。

作为旁注,委托也将持有对象实例的引用 – “this” – 即使在删除了对类a的所有其他引用之后。 因此,为了确保对类进行垃圾回收,应删除非静态成员方法(或在一个范围内创建的委托)的所有委托。

嗯,委托是一种类型。 委托类型的变量可以引用或指向函数。

这为您提供了一种调用方法的间接方法,因此可以在运行时选择方法。 因此,您可以拥有包含方法的变量,参数和属性。 这些属性称为事件。

还有一个代码示例,完整:

  delegate void ADelegate(); // the delegate type void Foo() { ... } // a compatible method void Bar() { ... } // a compatible method void Main() { ADelegate funcPtr; // a delegate variable if (aCondition) funcPtr = Foo; // note: _not_ Foo(), Foo is not executed here else funcPtr = Bar; funcPtr(); // calls Foo or Bar depending on aCondition } 

使用委托变量并不常见。 但是您可以使用委托参数例如Sort方法来选择升序或降序排序。

  delegate int Compare(MyClass a, MyClass b); // the delegate type void int CompareUp(MyClass a, MyClass b) { ... } void int CompareDn(MyClass a, MyClass b) { ... } void Sort(MyClass[] data, Compare comparer) { ... } 

您可能知道基于委托的(特殊类型)属性事件。

委托是指向某处定义的方法的函数指针。

假设您有一个BankAccount类,并且只要他/她的余额低于100美元,您就必须向客户发送电子邮件。 然后,自然趋势是在Balance属性设置器中添加一个检查,以查看客户的余额是否低于100美元,如果是,则触发电子邮件。 但这种设计并不灵活。

上述方法的缺点:

将来,肯定会要求向客户发送短信而不是电子邮件。 很少有客户选择电子邮件和短信。 因此,无论何时您需要通知客户任何更改,您都将修改BankAccount类。 这违反了OPEN FOR EXTENSION和CLOSED FOR MODIFICATION坚固的设计原则。

使用DELEGATES的替代解决方案:

  1. 定义NotifyCustomer()方法,该方法处理在BankAccount类之外向客户发送有关低余额的通知。

  2. 修改BankAccount类以定义委托并在构造函数中接受它的值。

  3. 在创建BankAccount类时,传入在步骤1中创建的NotifyCustomer()方法。

  4. 在BankAccount的类Balance setter中,检查余额是否低于100美元。 如果是,请调用委托。

  5. 调用BankAccount类外部定义的NotifyCustomer()方法,从而导致发送定义的通知。

将来,如果有新的方式通知客户,则BankAccount类不需要进行任何更改。

使用代表的设计优点:

  1. LOOSE COUPLINGBankAccount类不知道通知客户的硬编码逻辑。

  2. 遵循OPEN FOR EXTENSION和CLOSED FOR MODIFICATION原则 :每当通知客户的媒介发生变化时,您无需更改BankAccount类。 所以现在你会自豪地说你的BankAccount类设计遵循设计原则。

如果您想通过示例了解有关委托的更多信息,请阅读什么是委托以及我们需要它们的原因。

它的反转原理。 通常,您编写调用方法的代码,并且在编写代码时已知您调用的方法。 代理允许您匿名调用方法。 也就是说,您不知道程序运行时调用的实际方法。

它有助于分离应用程序不同部分的关注点。 因此,您可以拥有一些在数据存储上执行任务的代码。 您可能有其他代码来处理数据。 数据上的过程不需要知道数据存储的结构,数据存储不应该依赖于数据的使用。

可以假设关于与数据存储的结构无关的数据的某些事情来编写处理代码。 这样,我们可以更改数据存储的结构,而不用担心影响数据上的进程。

您可以将委托视为将代码视为数据的一种方式。 如果您创建委托,则它是一种类型。 这种类型的变量可能指向特定的方法(符合委托定义)。

这意味着您可以将一段代码视为数据,例如将其传递给方法。 由于委托指向代码(或null),您也可以通过变量调用它指向的代码。

这允许一些非常有用的模式。 典型的例子是如何对集合进行排序。 通过允许调用者提供实现对特定元素进行排序的代理的委托,排序方法不必知道任何相关内容。

对于LINQ,很多方法都使用了相同的思想。 即你传入一个处理某个特定任务的委托(或更常见的lambda),并且有问题的LINQ方法将调用它来完成任务。

委托是一种引用类型,它通过委托实例调用单个/多个方法。 它包含方法的引用.Delegates可用于在单个事件上处理(调用/调用)多个方法。 委托可用于定义异步方法。 这是委托的示例首先我们创建一个类。我们在其中声明委托。我们在类中创建一个方法来调用委托。

 public class simpleinterest { public delegate void intcal(double i); //declare delegate public event intcal interest; //create delegate object public void calculate(double p, double n,double r) { interest(p*n*r/100); //invoke delegate } } 

在我们的程序中,我们进行映射。即我们指定在调用委托时将触发哪个事件。

  private void btn_Click(object sender, RoutedEventArgs e) { simpleinterest s1 = new simpleinterest(); s1.interest+=new simpleinterest.intcal(s1_interest);//mapping s1.calculate(1000,3,10); } void s1_interest(double r) { MessageBox.Show("Amount:" + r.ToString()); } 

1)首先,您必须了解为什么/何时需要代表,它解决的问题是什么。

根据我的经验,我主要使用它们来允许用户自定义对象的行为

Immagine一个Grid组件,允许开发人员自定义每个列的呈现方式。 例如,当数字低于零时,您想要写一个红色值。

创建Grid的开发人员不知道用户如何自定义输出,因此需要一种机制让组件的用户向组件中注入一些逻辑

2)然后你必须了解代表的工作方式

令人困惑的是你必须编写的奇怪代码,以及你必须做同样事情的许多方法

这是网格类:

 // the grid let the programmer that will use it to customize the output public class Grid{ // 1) First I declare only the interface of the delegate public delegate String ValueFormatterDelegate(String v); // 2) I declare a handler of the implementation of the delegate public ValueFormatterDelegate ValueFormatterHandler; // 3) I call the handler inside the Print method public void Print(String x){ Console.WriteLine( ValueFormatterHandler.Invoke(x) ); } } 

// 1)首先,我只声明委托公共委托的接口String ValueFormatterDelegate(String v);

请注意,这与普通方法类似,但是:

  • 它有一个委托关键字
  • 它没有实现

用这种方式我说:“将输出格式化的方法有这个接口:它将一个字符串作为输入,它将输出一个字符串”

它记得我对接口方法的定义。

// 2)我声明了委托公共ValueFormatterDelegate ValueFormatterHandler的实现的处理程序;

现在我必须创建一个委托类型的属性来处理这个方法的实现。

// 3)我在Print方法中调用处理程序public void Print(String x){Console.WriteLine(ValueFormatterHandler.Invoke(x)); }

在Print方法中,我可以使用将链接真实实现的处理程序。

ValueFormatterHandler的类型为ValueFormatterDelegate,ValueFormatterDelegate是ad delegate,而.Invoke是委托类型的方法

这是一个使用我的Grid类的程序,可以动态地对其进行个性化。 这里的问题是你必须做同样事情的许多方法。

 using System; public class Program{ public static void Main(){ var printer = new Printer(); // METHOD 1 : link to a named method // here i link the handler of the delegate to a real method // the FormatXXX is a static method defined at the ed of this code printer.ValueFormatter = FormatXXX; // when i call Print("hello") printer.Print("hello"); // XXhelloXX // METHOD 2 : anonimous method // think at this like a method but without a name // FormatYY (String x ){ return "YY"+x+"YY"; }; // become // delegate (String x ){ return "YY"+x+"YY"; }; printer.ValueFormatter = delegate (String x ){ return "YY"+x+"YY"; }; printer.Print("hello"); // YYhelloYY // METHOD 3 : anonimous method using lambda // as you can note the type of parameter x is inferred from the delegate declaration // public delegate String ValueFormatterDelegate(String v); printer.ValueFormatter = (x)=>"KK" + x + "KK"; } public static String FormatXXX(String y){ return "XX"+ y +"XX"; } } 

委托是引用类型,委托是指方法。 这称为封装方法。 创建委托时,您指定方法签名和返回类型。 您可以使用该委托封装任何匹配方法。 您可以使用delegate关键字创建委托,然后是返回类型以及可以委派给它的方法的签名,如下所示:

 public delegate void HelloFunctionDelegate(string message); class Program { static void Main() { HelloFunctionDelegate del = new HelloFunctionDelegate(Hello); del("Hello from Delegate"); } public static void Hello(string strMessage) { Console.WriteLine(strMessage); } } 

来自Delegate的输出是Hello

在c#中延迟:它定义了它可以调用的方法的签名。换句话说,我们可以说它包装了它可以调用的方法的引用。 以下是 代表们 的用法

  1. 它提供了在.NET框架中实现回调function的机制。
  2. 它提供了顺序调用多个方法的function。
  3. 它具有实现异步方法调用的能力。

它支持静态和实例方法。

下面解释它是如何在内部工作的。

//这是代表们的声明。

 public delegate void DisplayNamme(string name); 

在运行时CLR为代理创建一个类,如下所示。

 public class DisplayNamme : System.MulticastDelegate{ // It is a contructor public DisplayNamme(Object @object, IntPtr method); // It is the method with the same prototype as defined in the source code. public void Invoke(String name); // This method allowing the callback to be asynchronouslly. public virtual IAsyncResult BeginInvoke(String name, AsyncCallback callback, Object @object); public virtual void EndInvoke(IAsyncResult result); } 

我们可以通过ILDasm.exe 工具 看到它使用此工具来破坏DLL。

构造函数有两个参数:IntPrt引用传递给函数的方法的名称,@ object引用隐式传递给构造函数的对象的引用。

CLR使用委托的Invoke方法来调用回调方法。

下面是使用委托的回调方法的实现。

 // Declare Delegates public delegate void DisplayNamme(string name); class Program { public static void getName(string name) { Console.WriteLine(name); } public static void ShowName(DisplayNamme dn, string name) { // callback method calling. We can call it in two ways. dn(name); // or explicitly dn.Invoke(name); } static void Main(string[] args) { DisplayNamme delDN = getName; Program.ShowName(delDN, "CallBack"); Console.ReadLine(); } } 

Delegate是一个引用类型变量,用于指向对方法的引用。 所有委托都派生自System.Delegate类。 例如,在Windows窗体或WPF中,方法事件与委托的概念一起使用。 这是在C#中使用delagates的示例C# 中的委托简介