何时&为什么使用委托?(复制)

时间:2022-12-22 20:52:19

This question already has an answer here:

这个问题已经有了答案:

I'm relatively new in C#, & I'm wondering when to use Delegates appropriately. they are widely used in events declaration , but when should I use them in my own code and why are they useful? why not to use something else?

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

I'm also wondering when I have to use delegates and I have no other alternative.

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

Thank you for the help!

谢谢你的帮助!

EDIT: I think I've found a necessary use of Delegates here

编辑:我想我已经在这里找到了必要的代表。

8 个解决方案

#1


221  

I agree with everything that is said already, just trying to put some other words on it.

我同意已经说过的每一件事,只是试着换句话说。

A delegate can be seen as a placeholder for a/some method(s).

委托可以被看作是某个方法的占位符。

By defining a delegate, you are saying to the user of your class, "Please feel free to assign, any method that matches this signature, to the delegate and it will be called each time my delegate is called".

通过定义一个委托,您对您的类的用户说,“请随意分配,任何匹配这个签名的方法,给委托,每次调用我的委托时都会调用它。”

Typical use is of course events. All the OnEventX delegate to the methods the user defines.

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

Delegates are useful to offer to the user of your objects some ability to customize their behavior. Most of the time, you can use other ways to achieve the same purpose and I do not believe you can ever be forced to create delegates. It is just the easiest way in some situations to get the thing done.

委托对于您的对象的用户提供一些定制他们行为的能力是很有用的。大多数时候,你可以用其他方法来达到同样的目的,我不相信你会*去创造代表。在某些情况下,这是最简单的方法。

#2


236  

A delegate is a reference to a method. Whereas objects can easily be sent as parameters into methods, constructor or whatever, methods are a bit more tricky. But every once in a while you might feel the need to send a method as a parameter to another method, and that's when you'll need delegates.

委托是对方法的引用。尽管对象可以很容易地作为参数发送到方法、构造函数或其他方法,但是方法要复杂一些。但是每隔一段时间,您可能会觉得需要将一个方法作为参数发送给另一个方法,而这正是您需要委托的时候。

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

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;
    }
  }
}

#3


133  

Say you want to write a procedure to integrate some real-valued function f (x) over some interval [a, b]. Say we want to use the 3-Point Gaussian method to do this (any will do, of course).

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

Ideally we want some function that looks like:

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

// '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;
}

So we can pass in any Integrand, f, and get its definite integral over the closed interval.

所以我们可以通过任何一个积分,f,得到它在闭区间上的定积分。

Just what type should Integrand be?

什么类型应该被整合?

Without Delegates

Well, without delegates, we'd need some sort of interface with a single method, say eval declared as follows:

嗯,没有委托,我们需要某种接口和一个方法,eval声明如下:

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

Then we'd need to create a whole bunch of classes implementing this interface, as follows:

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

// 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

Then to use them in our Gauss3 method, we need to invoke it as follows:

然后在我们的Gauss3方法中使用它们,我们需要调用它:

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

And Gauss3 needs to do the look like the following:

Gauss3需要这样做:

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

So we need to do all that just to use our arbitrary functions in Guass3.

我们需要做的就是在Guass3中使用任意函数。

With Delegates

public delegate double Integrand(double x);

Now we can define some static (or not) functions adhering to that prototype:

现在我们可以定义一些静态的(或不)函数,它遵循这个原型:

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);    
   }
}

No interfaces, no clunky .eval stuff, no object instantiation, just simple function-pointer like usage, for a simple task.

没有接口,没有笨拙的。eval的东西,没有对象实例化,只是简单的函数指针,比如用法,用于一个简单的任务。

Of course, delegates are more than just function pointers under the hood, but that's a separate issue (function chaining and events).

当然,委托不仅仅是在后台的函数指针,而是一个单独的问题(函数链接和事件)。

#4


25  

Delegates are extremely useful when wanting to declare a block of code that you want to pass around. For example when using a generic retry mechanism.

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

Pseudo:

伪:

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

Or when you want to do late evaluation of code blocks, like a function where you have some Transform action, and want to have a BeforeTransform and an AfterTransform action that you can evaluate within your Transform function, without having to know whether the BeginTransform is filled, or what it has to transform.

或者你什么时候想做后期评估代码块,像一个函数,你有一些变换动作,和想要一个BeforeTransform AfterTransform行动,你可以评估在你变换函数,而无需知道BeginTransform填充,或变换。

And of course when creating event handlers. You don't want to evaluate the code now, but only when needed, so you register a delegate that can be invoked when the event occurs.

当然,在创建事件处理程序时。您现在不希望对代码进行评估,但仅在需要时才进行评估,因此您注册了一个委托,当事件发生时可以调用该委托。

#5


20  

Delegates Overview

Delegates have the following properties:

代表具有以下特性:

  • Delegates are similar to C++ function pointers, but are type safe.
  • 委托类似于c++函数指针,但类型安全。
  • 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.
  • 方法不需要完全匹配委托签名。有关更多信息,请参见协方差和Contra方差。
  • 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.
  • c# 2.0引入了匿名方法的概念,该方法允许将代码块作为参数传递给一个单独定义的方法。

#6


19  

I've just go my head around these, and so I'll share an example as you already have descriptions but at the moment one advantage I see is to get around the Circular Reference style warnings where you can't have 2 projects referencing each other.

我只是在这些方面做了一些尝试,所以我将分享一个例子,你们已经有了描述,但目前我看到的一个优点是绕过了循环引用样式的警告,在那里你不能有两个项目相互引用。

Let's assume an application downloads an XML, and then saves the XML to a database.

让我们假设一个应用程序下载一个XML,然后将XML保存到数据库。

I have 2 projects here which build my solution: FTP and a SaveDatabase.

我有两个项目来构建我的解决方案:FTP和SaveDatabase。

So, our application starts by looking for any downloads and downloading the file(s) then it calls the SaveDatabase project.

因此,我们的应用程序首先查找任何下载并下载文件(s),然后调用SaveDatabase项目。

Now, our application needs to notify the FTP site when a file is saved to the database by uploading a file with Meta data (ignore why, it's a request from the owner of the FTP site). The issue is at what point and how? We need a new method called NotifyFtpComplete() but in which of our projects should it be saved too - FTP or SaveDatabase? Logically, the code should live in our FTP project. But, this would mean our NotifyFtpComplete will have to be triggered or, it will have to wait until the save is complete, and then query the database to ensure it is in there. What we need to do is tell our SaveDatabase project to call the NotifyFtpComplete() method direct but we can't; we'd get a ciruclar reference and the NotifyFtpComplete() is a private method. What a shame, this would have worked. Well, it can.

现在,我们的应用程序需要通过上传带有元数据的文件(忽略为什么,它是来自FTP站点所有者的请求)将文件保存到数据库时,需要通知FTP站点。问题是在什么时候,怎么做?我们需要一个名为NotifyFtpComplete()的新方法,但是在哪个项目中,它应该被保存——FTP还是SaveDatabase?从逻辑上讲,代码应该存在于我们的FTP项目中。但是,这意味着我们的NotifyFtpComplete将不得不被触发,或者,它必须等到save完成之后,然后查询数据库以确保它在那里。我们需要做的是告诉SaveDatabase项目调用NotifyFtpComplete()方法,但我们不能;我们会得到ciruclar引用,而NotifyFtpComplete()是一个私有方法。真可惜,这是可行的。好,可以。

During our application's code, we would have passed parameters between methods, but what if one of those parameters was the NotifyFtpComplete method. Yup, we pass the method, with all of the code inside as well. This would mean we could execute the method at any point, from any project. Well, this is what the delegate is. This means, we can pass the NotifyFtpComplete() method as a parameter to our SaveDatabase() class. At the point it saves, it simply executes the delegate.

在我们的应用程序代码中,我们会在方法之间传递参数,但是如果其中一个参数是NotifyFtpComplete方法会怎么样。是的,我们通过了这个方法,里面还有所有的代码。这意味着我们可以在任何时候,从任何项目中执行该方法。这就是委托。这意味着,我们可以将NotifyFtpComplete()方法作为参数传递给SaveDatabase()类。在它保存的时候,它只是执行委托。

See if this crude example helps (pseudo code). We will also assume that the application starts with the Begin() method of the FTP class.

看看这个粗糙的例子是否有帮助(pseudo代码)。我们还将假设应用程序从FTP类的Begin()方法开始。

class FTP
{
    public void Begin()
    {
        string filePath = DownloadFileFromFtpAndReturnPathName();

        SaveDatabase sd = new SaveDatabase();
        sd.Begin(filePath, NotifyFtpComplete());
    }

    private void NotifyFtpComplete()
    {
        //Code to send file to FTP site
    }
}


class SaveDatabase
{
    private void Begin(string filePath, delegateType NotifyJobComplete())
    {
        SaveToTheDatabase(filePath);

        //InvokeTheDelegate - here we can execute the NotifyJobComplete method at our preferred moment in the application, despite the method being private and belonging to a different class. 
        NotifyJobComplete.Invoke();
    }
}

So, with that explained, we can do it for real now with this Console Application using C#

这样,我们就可以用c#来实现这个控制台应用程序。

using System;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            //Note, this NotifyDelegate type is defined in the SaveToDatabase project
            NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);

            SaveToDatabase sd = new SaveToDatabase();            
            sd.Start(nofityDelegate);
            Console.ReadKey();
        }

        //this is the method which will be delegated - the only thing it has in common with the NofityDelegate is that it takes 0 parameters and that it returns void. However, it is these 2 which are essential. It is really important to notice that it writes a variable which, due to no constructor, has not yet been called (so _notice is not initialized yet). 
    private static void NotifyIfComplete()
    {
        Console.WriteLine(_notice);
    }

    private static string _notice = "Notified";
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegate nd)
        {
            Console.WriteLine("Yes, I shouldn't write to the console from here, it's just to demonstrate the code executed.");
            Console.WriteLine("SaveToDatabase Complete");
            Console.WriteLine(" ");
            nd.Invoke();
        }
    }
    public delegate void NotifyDelegate();
}

I suggest you step through the code and see when _notice is called and when the method (delegate) is called as this, I hope, will make things very clear.

我建议您跨出代码,查看何时调用_notice,何时调用该方法(委托),我希望这将使事情变得非常清楚。

However, lastly, we can make it more useful by changing the delegate type to include a parameter.

但是,最后,我们可以通过改变委托类型来包含一个参数来使它更有用。

using System.Text;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            SaveToDatabase sd = new SaveToDatabase();

//Please note, that although NotifyIfComplete() takes a string parameter, we do not declare it - all we want to do is tell C# where the method is so it can be referenced later - we will pass the paramater later.
            NotifyDelegateWithMessage nofityDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(nofityDelegateWithMessage);

            Console.ReadKey();
        }

        private static void NotifyIfComplete(string message)
        {
            Console.WriteLine(message);
        }
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegateWithMessage nd)
        {
            //To simulate a saving fail or success, I'm just going to check the current time (well, the seconds) and store the value as variable.
            string message = string.Empty;
            if (DateTime.Now.Second > 30)
                message = "Saved";
            else
                message = "Failed";

            //It is at this point we pass the parameter to our method.
            nd.Invoke(message);
        }
    }

    public delegate void NotifyDelegateWithMessage(string message);
}

#7


9  

I consider delegates to be Anonymous Interfaces. In many cases you can use them whenever you need an interface with a single method, but you don't want the overhead of defining that interface.

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

#8


3  

A delegate is a simple class that is used to point to methods with a specific signature, becoming essentially a type-safe function pointer. A delegate's purpose is to facilitate a call back to another method (or methods), after one has been completed, in a structured way.

委托是一个简单的类,用于指向具有特定签名的方法,它本质上是一个类型安全的函数指针。委托的目的是为了方便调用另一种方法(或方法),在完成之后,以结构化的方式。

While it could be possible to create an extensive set of code to perform this functionality, you don’t need too. You can use a delegate.

虽然可以创建大量的代码来执行此功能,但您也不需要。你可以使用委托。

Creating a delegate is easy to do. Identify the class as a delegate with the "delegate" keyword. Then specify the signature of the type.

创建委托很容易。用“委托”关键字标识类。然后指定类型的签名。

#1


221  

I agree with everything that is said already, just trying to put some other words on it.

我同意已经说过的每一件事,只是试着换句话说。

A delegate can be seen as a placeholder for a/some method(s).

委托可以被看作是某个方法的占位符。

By defining a delegate, you are saying to the user of your class, "Please feel free to assign, any method that matches this signature, to the delegate and it will be called each time my delegate is called".

通过定义一个委托,您对您的类的用户说,“请随意分配,任何匹配这个签名的方法,给委托,每次调用我的委托时都会调用它。”

Typical use is of course events. All the OnEventX delegate to the methods the user defines.

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

Delegates are useful to offer to the user of your objects some ability to customize their behavior. Most of the time, you can use other ways to achieve the same purpose and I do not believe you can ever be forced to create delegates. It is just the easiest way in some situations to get the thing done.

委托对于您的对象的用户提供一些定制他们行为的能力是很有用的。大多数时候,你可以用其他方法来达到同样的目的,我不相信你会*去创造代表。在某些情况下,这是最简单的方法。

#2


236  

A delegate is a reference to a method. Whereas objects can easily be sent as parameters into methods, constructor or whatever, methods are a bit more tricky. But every once in a while you might feel the need to send a method as a parameter to another method, and that's when you'll need delegates.

委托是对方法的引用。尽管对象可以很容易地作为参数发送到方法、构造函数或其他方法,但是方法要复杂一些。但是每隔一段时间,您可能会觉得需要将一个方法作为参数发送给另一个方法,而这正是您需要委托的时候。

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

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;
    }
  }
}

#3


133  

Say you want to write a procedure to integrate some real-valued function f (x) over some interval [a, b]. Say we want to use the 3-Point Gaussian method to do this (any will do, of course).

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

Ideally we want some function that looks like:

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

// '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;
}

So we can pass in any Integrand, f, and get its definite integral over the closed interval.

所以我们可以通过任何一个积分,f,得到它在闭区间上的定积分。

Just what type should Integrand be?

什么类型应该被整合?

Without Delegates

Well, without delegates, we'd need some sort of interface with a single method, say eval declared as follows:

嗯,没有委托,我们需要某种接口和一个方法,eval声明如下:

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

Then we'd need to create a whole bunch of classes implementing this interface, as follows:

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

// 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

Then to use them in our Gauss3 method, we need to invoke it as follows:

然后在我们的Gauss3方法中使用它们,我们需要调用它:

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

And Gauss3 needs to do the look like the following:

Gauss3需要这样做:

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

So we need to do all that just to use our arbitrary functions in Guass3.

我们需要做的就是在Guass3中使用任意函数。

With Delegates

public delegate double Integrand(double x);

Now we can define some static (or not) functions adhering to that prototype:

现在我们可以定义一些静态的(或不)函数,它遵循这个原型:

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);    
   }
}

No interfaces, no clunky .eval stuff, no object instantiation, just simple function-pointer like usage, for a simple task.

没有接口,没有笨拙的。eval的东西,没有对象实例化,只是简单的函数指针,比如用法,用于一个简单的任务。

Of course, delegates are more than just function pointers under the hood, but that's a separate issue (function chaining and events).

当然,委托不仅仅是在后台的函数指针,而是一个单独的问题(函数链接和事件)。

#4


25  

Delegates are extremely useful when wanting to declare a block of code that you want to pass around. For example when using a generic retry mechanism.

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

Pseudo:

伪:

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

Or when you want to do late evaluation of code blocks, like a function where you have some Transform action, and want to have a BeforeTransform and an AfterTransform action that you can evaluate within your Transform function, without having to know whether the BeginTransform is filled, or what it has to transform.

或者你什么时候想做后期评估代码块,像一个函数,你有一些变换动作,和想要一个BeforeTransform AfterTransform行动,你可以评估在你变换函数,而无需知道BeginTransform填充,或变换。

And of course when creating event handlers. You don't want to evaluate the code now, but only when needed, so you register a delegate that can be invoked when the event occurs.

当然,在创建事件处理程序时。您现在不希望对代码进行评估,但仅在需要时才进行评估,因此您注册了一个委托,当事件发生时可以调用该委托。

#5


20  

Delegates Overview

Delegates have the following properties:

代表具有以下特性:

  • Delegates are similar to C++ function pointers, but are type safe.
  • 委托类似于c++函数指针,但类型安全。
  • 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.
  • 方法不需要完全匹配委托签名。有关更多信息,请参见协方差和Contra方差。
  • 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.
  • c# 2.0引入了匿名方法的概念,该方法允许将代码块作为参数传递给一个单独定义的方法。

#6


19  

I've just go my head around these, and so I'll share an example as you already have descriptions but at the moment one advantage I see is to get around the Circular Reference style warnings where you can't have 2 projects referencing each other.

我只是在这些方面做了一些尝试,所以我将分享一个例子,你们已经有了描述,但目前我看到的一个优点是绕过了循环引用样式的警告,在那里你不能有两个项目相互引用。

Let's assume an application downloads an XML, and then saves the XML to a database.

让我们假设一个应用程序下载一个XML,然后将XML保存到数据库。

I have 2 projects here which build my solution: FTP and a SaveDatabase.

我有两个项目来构建我的解决方案:FTP和SaveDatabase。

So, our application starts by looking for any downloads and downloading the file(s) then it calls the SaveDatabase project.

因此,我们的应用程序首先查找任何下载并下载文件(s),然后调用SaveDatabase项目。

Now, our application needs to notify the FTP site when a file is saved to the database by uploading a file with Meta data (ignore why, it's a request from the owner of the FTP site). The issue is at what point and how? We need a new method called NotifyFtpComplete() but in which of our projects should it be saved too - FTP or SaveDatabase? Logically, the code should live in our FTP project. But, this would mean our NotifyFtpComplete will have to be triggered or, it will have to wait until the save is complete, and then query the database to ensure it is in there. What we need to do is tell our SaveDatabase project to call the NotifyFtpComplete() method direct but we can't; we'd get a ciruclar reference and the NotifyFtpComplete() is a private method. What a shame, this would have worked. Well, it can.

现在,我们的应用程序需要通过上传带有元数据的文件(忽略为什么,它是来自FTP站点所有者的请求)将文件保存到数据库时,需要通知FTP站点。问题是在什么时候,怎么做?我们需要一个名为NotifyFtpComplete()的新方法,但是在哪个项目中,它应该被保存——FTP还是SaveDatabase?从逻辑上讲,代码应该存在于我们的FTP项目中。但是,这意味着我们的NotifyFtpComplete将不得不被触发,或者,它必须等到save完成之后,然后查询数据库以确保它在那里。我们需要做的是告诉SaveDatabase项目调用NotifyFtpComplete()方法,但我们不能;我们会得到ciruclar引用,而NotifyFtpComplete()是一个私有方法。真可惜,这是可行的。好,可以。

During our application's code, we would have passed parameters between methods, but what if one of those parameters was the NotifyFtpComplete method. Yup, we pass the method, with all of the code inside as well. This would mean we could execute the method at any point, from any project. Well, this is what the delegate is. This means, we can pass the NotifyFtpComplete() method as a parameter to our SaveDatabase() class. At the point it saves, it simply executes the delegate.

在我们的应用程序代码中,我们会在方法之间传递参数,但是如果其中一个参数是NotifyFtpComplete方法会怎么样。是的,我们通过了这个方法,里面还有所有的代码。这意味着我们可以在任何时候,从任何项目中执行该方法。这就是委托。这意味着,我们可以将NotifyFtpComplete()方法作为参数传递给SaveDatabase()类。在它保存的时候,它只是执行委托。

See if this crude example helps (pseudo code). We will also assume that the application starts with the Begin() method of the FTP class.

看看这个粗糙的例子是否有帮助(pseudo代码)。我们还将假设应用程序从FTP类的Begin()方法开始。

class FTP
{
    public void Begin()
    {
        string filePath = DownloadFileFromFtpAndReturnPathName();

        SaveDatabase sd = new SaveDatabase();
        sd.Begin(filePath, NotifyFtpComplete());
    }

    private void NotifyFtpComplete()
    {
        //Code to send file to FTP site
    }
}


class SaveDatabase
{
    private void Begin(string filePath, delegateType NotifyJobComplete())
    {
        SaveToTheDatabase(filePath);

        //InvokeTheDelegate - here we can execute the NotifyJobComplete method at our preferred moment in the application, despite the method being private and belonging to a different class. 
        NotifyJobComplete.Invoke();
    }
}

So, with that explained, we can do it for real now with this Console Application using C#

这样,我们就可以用c#来实现这个控制台应用程序。

using System;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            //Note, this NotifyDelegate type is defined in the SaveToDatabase project
            NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);

            SaveToDatabase sd = new SaveToDatabase();            
            sd.Start(nofityDelegate);
            Console.ReadKey();
        }

        //this is the method which will be delegated - the only thing it has in common with the NofityDelegate is that it takes 0 parameters and that it returns void. However, it is these 2 which are essential. It is really important to notice that it writes a variable which, due to no constructor, has not yet been called (so _notice is not initialized yet). 
    private static void NotifyIfComplete()
    {
        Console.WriteLine(_notice);
    }

    private static string _notice = "Notified";
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegate nd)
        {
            Console.WriteLine("Yes, I shouldn't write to the console from here, it's just to demonstrate the code executed.");
            Console.WriteLine("SaveToDatabase Complete");
            Console.WriteLine(" ");
            nd.Invoke();
        }
    }
    public delegate void NotifyDelegate();
}

I suggest you step through the code and see when _notice is called and when the method (delegate) is called as this, I hope, will make things very clear.

我建议您跨出代码,查看何时调用_notice,何时调用该方法(委托),我希望这将使事情变得非常清楚。

However, lastly, we can make it more useful by changing the delegate type to include a parameter.

但是,最后,我们可以通过改变委托类型来包含一个参数来使它更有用。

using System.Text;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            SaveToDatabase sd = new SaveToDatabase();

//Please note, that although NotifyIfComplete() takes a string parameter, we do not declare it - all we want to do is tell C# where the method is so it can be referenced later - we will pass the paramater later.
            NotifyDelegateWithMessage nofityDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(nofityDelegateWithMessage);

            Console.ReadKey();
        }

        private static void NotifyIfComplete(string message)
        {
            Console.WriteLine(message);
        }
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegateWithMessage nd)
        {
            //To simulate a saving fail or success, I'm just going to check the current time (well, the seconds) and store the value as variable.
            string message = string.Empty;
            if (DateTime.Now.Second > 30)
                message = "Saved";
            else
                message = "Failed";

            //It is at this point we pass the parameter to our method.
            nd.Invoke(message);
        }
    }

    public delegate void NotifyDelegateWithMessage(string message);
}

#7


9  

I consider delegates to be Anonymous Interfaces. In many cases you can use them whenever you need an interface with a single method, but you don't want the overhead of defining that interface.

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

#8


3  

A delegate is a simple class that is used to point to methods with a specific signature, becoming essentially a type-safe function pointer. A delegate's purpose is to facilitate a call back to another method (or methods), after one has been completed, in a structured way.

委托是一个简单的类,用于指向具有特定签名的方法,它本质上是一个类型安全的函数指针。委托的目的是为了方便调用另一种方法(或方法),在完成之后,以结构化的方式。

While it could be possible to create an extensive set of code to perform this functionality, you don’t need too. You can use a delegate.

虽然可以创建大量的代码来执行此功能,但您也不需要。你可以使用委托。

Creating a delegate is easy to do. Identify the class as a delegate with the "delegate" keyword. Then specify the signature of the type.

创建委托很容易。用“委托”关键字标识类。然后指定类型的签名。