Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体

495
votes
answers
楼主
13 views
+10

何時以及為何使用代表?[重複]

這個問題在這裡已有答案: 我在哪裡使用代表?[已關閉] 8個答案 我在C#中比較新,我想知道何時適當地使用Delegates。它們被廣泛用於事件聲明中,但何時應該在我自己的代碼中使用它們,為什麼它們有用? 為什麼不用別的東西? 我也想知道何時必須使用代表,我別無選擇。 感謝您的幫助! 編輯:我想我已經在這裡找到了代表 的必要用法

Delegate是一個知道如何調用方法的對象。{定義來自C#In a Nutshell} - GibboK 2014年6月11日6:09

不它不是。這只是消息轉發。這不是代表團。這就是為什麼C#中的委託和事件存在很多混亂的原因 - 因為C#中沒有任何代表。saturnflyer.com/blog/jim/2012/07/06/ ... - Lloyd Sargent 2014年11月7日18:13

從教程:C#中的事件是類提供通知的一種方式......(例如,單擊按鈕)。使用委託聲明事件。我:因此,您需要Delegates在C#中實現一個監聽器。而已。MS已迫使所有人使用Delegates。 - Joe Cotton 2016年8月19日20:05

看看這篇文章codeproject.com/Articles/85296 / ... - 開發人員10月3日'18在4:43

沙发
+2720

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("

");
    }

    //==========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;
    }
  }
}
板凳
+1420

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).

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.

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:

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

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:

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.

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.

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

地板
+270

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.

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.

4楼
+210

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.
5楼
+210

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.

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

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

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.

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.

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

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#

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.

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 notifyDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(notifyDelegateWithMessage );

            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);
}
6楼
+90

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.

7楼
+30

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.

discard