Ant*_*ton 27
考虑委托的一种方法就像对函数的引用.例如,假设您在窗口中有一个按钮,并且您希望在单击按钮时发生某些事情.您可以将代理附加到按钮的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");
}
}
Run Code Online (Sandbox Code Playgroud)
请注意我如何使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");
}
}
Run Code Online (Sandbox Code Playgroud)
现在,委托包含对函数"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");
});
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们创建了一个匿名委托 - 一个没有明确名称的函数.引用此函数的唯一方法是使用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
}
Run Code Online (Sandbox Code Playgroud)
使用委托变量并不常见.但是您可以使用委托参数例如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) { ... }
Run Code Online (Sandbox Code Playgroud)
您可能知道基于委托的(特殊类型)属性事件.
1)首先,您必须了解为什么/何时需要委托,它解决的问题是什么。
根据我的经验,我主要使用它们来允许用户自定义对象的行为。
想象一个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) );
}
}
Run Code Online (Sandbox Code Playgroud)
// 1) 首先我只声明委托 public delegate String ValueFormatterDelegate(String v) 的接口;
请注意,这就像一个正常的方法,但是:
这样我说:“格式化输出的方法有这个接口:它将一个字符串作为输入,它会输出一个字符串”
它记住了我对接口方法的定义。
// 2) 我声明了一个代理实现的处理程序 public ValueFormatterDelegate ValueFormatterHandler;
现在我必须创建一个委托类型的属性来处理这个方法的实现。
// 3) 我在 Print 方法中调用处理程序 public void Print(String x){ Console.WriteLine( ValueFormatterHandler.Invoke(x) ); }
在 Print 方法中,我可以使用将链接实际实现的处理程序。
ValueFormatterHandler 是 ValueFormatterDelegate 类型,ValueFormatterDelegate 是广告委托,.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";
}
}
Run Code Online (Sandbox Code Playgroud)