C#中的代表

Geo*_*lva 23 c# delegates

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

有人能用"普通英语"向我解释一下吗?当然!代码的例子会有所帮助,但我认为我需要更多关于它如何/为何起作用的描述.

编辑:

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

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

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

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的所有其他引用之后.因此,为了确保对类进行垃圾回收,应删除非静态成员方法(或在一个范围内创建的委托)的所有委托.


Hen*_*man 8

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

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

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

   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)

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


Mar*_*oli 5

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)