我对c#比较陌生,我想知道什么时候应该适当地使用委托。 它们在事件声明中被广泛使用,但是什么时候应该在我自己的代码中使用它们,为什么它们有用?为什么不用别的东西呢?

我也想知道什么时候我必须使用委托,我没有其他的选择。

谢谢你的帮助!

编辑:我认为我在这里找到了委托的必要用途


当前回答

Delegates Overview Delegates have the following properties: Delegates are similar to C++ function pointers, but are type safe. Delegates allow methods to be passed as parameters. Delegates can be used to define callback methods. Delegates can be chained together; for example, multiple methods can be called on a single event. Methods don't need to match the delegate signature exactly. For more information, see Covariance and Contra variance. C# version 2.0 introduces the concept of Anonymous Methods, which permit code blocks to be passed as parameters in place of a separately defined method.

其他回答

我同意已经说过的一切,只是试着用另一种说法来解释。

委托可以被视为一个或多个方法的占位符。

通过定义委托,您就在对类的用户说:“请随意将与此签名匹配的任何方法分配给委托,并且每次调用我的委托时都会调用它”。

典型的使用当然是事件。所有的OnEventX委托给用户定义的方法。

委托非常有用,可以为对象的用户提供一些自定义行为的能力。 大多数时候,你可以使用其他方法来达到同样的目的,我不相信你会被强迫创建委托。在某些情况下,这是完成任务最简单的方法。

当你想要声明你想要传递的代码块时,委托是非常有用的。例如,当使用通用重试机制时。

伪:

function Retry(Delegate func, int numberOfTimes)
    try
    {
       func.Invoke();
    }
    catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }

或者当你想要做代码块的后期计算,比如你有一些Transform动作的函数,并且想要有一个BeforeTransform和一个AfterTransform动作,你可以在你的Transform函数中计算,而不需要知道BeginTransform是否被填充,或者它必须转换什么。

当然,在创建事件处理程序时也是如此。您不希望现在计算代码,而只希望在需要时计算代码,因此您可以注册一个可以在事件发生时调用的委托。

委托是对方法的引用。虽然对象可以很容易地作为参数发送到方法、构造函数或其他方法中,但方法有点棘手。但每隔一段时间你可能会觉得需要将一个方法作为参数发送给另一个方法,这时你就需要委托。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegateApp {

  /// <summary>
  /// A class to define a person
  /// </summary>
  public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
  }

  class Program {
    //Our delegate
    public delegate bool FilterDelegate(Person p);

    static void Main(string[] args) {

      //Create 4 Person objects
      Person p1 = new Person() { Name = "John", Age = 41 };
      Person p2 = new Person() { Name = "Jane", Age = 69 };
      Person p3 = new Person() { Name = "Jake", Age = 12 };
      Person p4 = new Person() { Name = "Jessie", Age = 25 };

      //Create a list of Person objects and fill it
      List<Person> people = new List<Person>() { p1, p2, p3, p4 };

      //Invoke DisplayPeople using appropriate delegate
      DisplayPeople("Children:", people, IsChild);
      DisplayPeople("Adults:", people, IsAdult);
      DisplayPeople("Seniors:", people, IsSenior);

      Console.Read();
    }

    /// <summary>
    /// A method to filter out the people you need
    /// </summary>
    /// <param name="people">A list of people</param>
    /// <param name="filter">A filter</param>
    /// <returns>A filtered list</returns>
    static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
      Console.WriteLine(title);

      foreach (Person p in people) {
        if (filter(p)) {
          Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
        }
      }

      Console.Write("\n\n");
    }

    //==========FILTERS===================
    static bool IsChild(Person p) {
      return p.Age < 18;
    }

    static bool IsAdult(Person p) {
      return p.Age >= 18;
    }

    static bool IsSenior(Person p) {
      return p.Age >= 65;
    }
  }
}

输出:

Children:
Jake, 12 years old


Adults:
John, 41 years old
Jane, 69 years old
Jessie, 25 years old


Seniors:
Jane, 69 years old

假设你想写一个过程,在某个区间[a, b]上对某个实值函数f (x)积分。假设我们想要使用3点高斯方法来做到这一点(当然,任何方法都可以)。

理想情况下,我们想要这样的函数:

// 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals.
static double Gauss3(Integrand f, double a, double b, int n) {
  double res = 0;

  // compute result
  // ...

  return res;
}

我们可以传入任意被积函数f,得到它在闭区间上的定积分。

被积函数应该是什么类型?

没有代表

好吧,如果没有委托,我们需要某种只有一个方法的接口,比如eval声明如下:

// Interface describing real-valued functions of one variable.
interface Integrand {
  double eval(double x);
}

然后我们需要创建一大堆实现这个接口的类,如下所示:

// Some function
class MyFunc1 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// Some other function
class MyFunc2 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// etc

然后在我们的Gauss3方法中使用它们,我们需要像下面这样调用它:

double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);

Gauss3需要像下面这样做:

static double Gauss3(Integrand f, double a, double b, int n) {
  // Use the integrand passed in:
  f.eval(x);
}

所以我们需要做所有这些只是为了使用我们在Guass3中的任意函数。

与代表

public delegate double Integrand(double x);

现在我们可以定义一些静态(或非静态)函数来继承这个原型:

class Program {
   public delegate double Integrand(double x);   
   // Define implementations to above delegate 
   // with similar input and output types
   static double MyFunc1(double x) { /* ... */ }
   static double MyFunc2(double x) { /* ... */ }
   // ... etc ...

   public static double Gauss3(Integrand f, ...) { 
      // Now just call the function naturally, no f.eval() stuff.
      double a = f(x); 
      // ...
   }

   // Let's use it
   static void Main() {
     // Just pass the function in naturally (well, its reference).
     double res = Gauss3(MyFunc1, a, b, n);
     double res = Gauss3(MyFunc2, a, b, n);    
   }
}

没有接口,没有笨重的.eval,没有对象实例化,对于一个简单的任务,只有类似函数指针的简单用法。

当然,委托不仅仅是底层的函数指针,但这是一个单独的问题(函数链和事件)。

我认为委托是匿名接口。在许多情况下,只要需要带有单个方法的接口,就可以使用它们,但不希望定义该接口的开销。