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 | 繁体 | 简体


4 questions online user: 26

190
votes
answers
30 views
+10

Difference Between Invoke and DynamicInvoke

What is the difference between Invoke and DynamicInvoke in delegates? Please give me some code example which explain difference between that two methods.

up vote 190 down vote accepted favorite
沙发
+1900
+50

當你有一個委託實例時,你可能知道確切的類型,或者你可能只知道它是 Delegate 如果你知道確切的類型,你可以使用 Invoke ,這是非常快 - 所有內容都已經過預先驗證。例如:

  Func< int,int> 兩次= x => x * 2; int i = 3; int j =兩次.Invoke(i); //或只是:int j =兩次(i);   

然而!如果你只知道它是 Delegate ,它必須手動解析參數等 - 這可能涉及拆箱等 - 很多反思正在進行中。例如:

 委託slowTwice =兩次; //這仍然是同一個委託實例對象[] args = {i}; object result = slowTwice.DynamicInvoke(args);   

注意我已經編寫了 args ,以明確涉及 object [] 這裡有很多額外的成本:

  • 數組
  • 驗證傳遞的參數是否適合實際的 MethodInfo
  • 必要時取消裝箱等
  • reflection-invoke
  • 然後調用者需要做一些事情來處理返回值

    基本上,盡可能避免 DynamicInvoke Invoke 總是更可取的,除非您擁有 Delegate object [] < / code>。

    對於性能比較,調試器外部的發布模式(控制台exe)中的以下內容打印:

      Invoke:19ms DynamicInvoke:3813ms   

    代碼:

      Func&lt; int,int&gt; 兩次= x =&gt; x * 2; const int LOOP = 5000000; // 5M var watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){two.Invoke(3); } watch.Stop(); Console.WriteLine(“Invoke:{0} ms”,watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds);   打印模式外的發布模式(控制台exe)中的以下內容打印: 
     調用:19ms DynamicInvoke:3813ms   

    代碼: <預> <代碼>函數功能&LT; INT,INT&GT; 兩次= x =&gt; x * 2; const int LOOP = 5000000; // 5M var watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){two.Invoke(3); } watch.Stop(); Console.WriteLine(“Invoke:{0} ms”,watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds); 打印模式外的發布模式(控制台exe)中的以下內容打印:

     調用:19ms DynamicInvoke:3813ms   

    代碼: <預> <代碼>函數功能&LT; INT,INT&GT; 兩次= x =&gt; x * 2; const int LOOP = 5000000; // 5M var watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){two.Invoke(3); } watch.Stop(); Console.WriteLine(“Invoke:{0} ms”,watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds); / pre>

    代碼:

      Func&lt; int,int&gt; 兩次= x =&gt; x * 2; const int LOOP = 5000000; // 5M var watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){two.Invoke(3); } watch.Stop(); Console.WriteLine(“Invoke:{0} ms”,watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds);   / pre> 

    代碼:

      Func&lt; int,int&gt; 兩次= x =&gt; x * 2; const int LOOP = 5000000; // 5M var watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){two.Invoke(3); } watch.Stop(); Console.WriteLine(“Invoke:{0} ms”,watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds);   ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds);   ElapsedMilliseconds); watch = Stopwatch.StartNew(); for(int i = 0; i&lt; LOOP; i ++){twice.DynamicInvoke(3); } watch.Stop(); Console.WriteLine(“DynamicInvoke:{0} ms”,watch.ElapsedMilliseconds);  
         
    			
            

這是否意味著在使用的情況下,DynamicInvoke編譯器會產生更多的IL代碼來處理委託調用? - testCoder 12年12月12日11:54

@testCoder不,它會使用反射 - 馬克格拉維爾♦12年12月12日11:55

謝謝,非常充分的解釋。 - testCoder 12年12月12日中午12點

@MarcGravell當我在一個引發事件的方法中嘗試這個時,我接收的第一個方法調用大概是0,7766毫秒,但第二個大概是0,0568毫秒。當第一個是Invoke時,它需要比DynamicInvoke更長的時間,反之亦然。當我嘗試使用1循環的示例並查看ms Invoke:0,0478ms,DynamicInvoke:0,053ms。你為什麼要比他們多打1個電話?為什麼第一個需要比第二個函數調用更長的時間? - uzay95 16年12月18日13:40

@ uzay95對方法的第一次調用導致CLR進行JIT編譯 - 這適用於在進程啟動後第一次調用它時的任何方法。在這種情況下,您可以執行以下三種操作之一:(1)多次運行該方法,以便第一次調用所花費的時間在最終結果中變得微不足道,(2)直到您之後才開始測量已經調用過該方法一次,或者(3)使用ngen.exe(overkill)。這篇文章解釋得很好... stackoverflow.com/questions/4446203/ - 廣達2017年5月31日3:16

0
votes
answers
49 views
+10

發送按鈕上的值點擊從UIView到它的超級視圖的超級視圖(UIViewController)-iOS

0

我有一個叫做HomeViewController的ViewController。它的子視圖是(UIView)。 ActivityView的子視圖是ActivityCard(UIView)。在ActivityCard中,我有一個名爲Enter的按鈕。在它的點擊我需要將所有文本域的值從ActivityCard xib發送到HomeViewController並從那裏調用一個API。我以前從未與xib合作過,因此無法管理這種情況。任何幫助將不勝感激!發送按鈕上的值點擊從UIView到它的超級視圖的超級視圖(UIViewController)-iOS

編輯:

ActivityCard.h

@interface ActivityCard : UIView 


- (IBAction)actionEnterClick:(id)sender; 
@property (weak, nonatomic) IBOutlet UILabel *lblActivityCount; 
@property (weak, nonatomic) IBOutlet UIButton *enterButton; 

@end 

ActivityView.h

@interface ActivityView : UIView 

@property (strong) ActivityCard *card; 
@property (weak, nonatomic) IBOutlet iCarousel *carousel; 

@end 

ActivityView.m

- (UIView *)carousel:(__unused iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view 
{ 
if (view == nil) 
    { 
     _card = (ActivityCard*) [[[NSBundle mainBundle] loadNibNamed:@"ActivityCard" owner:nil options:nil] objectAtIndex:0]; 

     _card.frame = CGRectMake(0, 0, 280.0, 350.0); 
     _card.layer.cornerRadius = 10.0f; 
     view = _card; 
    } 
    return view; 
} 

HomeViewController.h

@interface HomeViewController : UIViewController 

@property (strong) ActivityView *viewActivity; 

@end 

HomeViewController.m

_viewActivity = (ActivityView*) [[[NSBundle mainBundle] loadNibNamed:@"ActivityView" owner:nil options:nil] objectAtIndex:0]; 
    _viewActivity.frame = CGRectMake(0.0f, 0.0f, _viewContainer.frame.size.width, _viewContainer.frame.size.height); 
    [_viewActivity updateActivityUI]; 

    for (UIView *child in _viewContainer.subviews) { 
     [child removeFromSuperview]; 
    } 
    [_viewContainer addSubview:_viewActivity]; 

    [_viewActivity.card.enterButton addTarget:self action:@selector(enterButtonDidTouch) forControlEvents:UIControlEventTouchUpInside]; 

    [_viewActivity.card.lblActivityCount setText:@"11"]; 

,當我們需要加載一個按鈕點擊ActivityView此代碼已被寫入。

沙发
0
1

有很多方法可以做到這一點,我會告訴你一種方式,我認爲這是最簡單的方法。

  • ActivityCard.h
  • HomeViewController添加動作爲您Enter Button使用下面我的代碼創建Enter Button您所有的文本框的propertyproperty

    [self.activityView.cardView enterButton addTarget:self action:@selector(enterButtonDidTouch) forControlEvents:UIControlEventTouchUpInside]; 
    
  • HomeViewController.menterButtonDidTouch

    - (void)enterButtonDidTouch { 
        NSString *textOfTextField1 = self.activityView.activityCard.textField1.text; 
        // With the same way you can get others text of text fields and use them 
    } 
    
+0

的方法在某種程度上是沒有得到所謂的創建方法。任何我可能會缺少的東西? – Dia

+0

當我嘗試將標籤文本從HomeViewController設置爲ActivityCard時,它沒有正確設置值。我試過了:[_viewActivity.card.lblActivityCount setText:@「11」]; – Dia

+0

正如我所見,你做對了。你能告訴我你是如何實施它們的嗎? @Dia – trungduc

0
votes
answers
41 views
+10

從一個線程

5

這加快實時更新桂是我已經使用多年接收網絡數據,並在我的GUI使用它(對話,形成等)的技術。從一個線程

public delegate void mydelegate(Byte[] message); 

    public ReceiveEngineCS(String LocalIpIn, String ReceiveFromIp, mydelegate d) 
    { 
     this.m_LocalIpIn = LocalIpIn; 
     this.m_ReceiveFromIp = ReceiveFromIp; 
     m_MainCallback = d; 
     SetupReceive(); 
     m_Running = true; 
     //Create the Track receive thread and pass the parent (this) 
     m_RtdirReceiveThread = new Thread(new ParameterizedThreadStart(MessageRecieveThread)); 
     m_RtdirReceiveThread.Start(this); 
    } 

    private void MessageRecieveThread(System.Object obj) 
    { 
     ReceiveEngineCS parent = (ReceiveEngineCS)obj; 

     while(parent.m_Running) 
     { 
       Byte[] receiveBytes = new Byte[1500]; 
       try 
       { 
        receiveBytes = parent.m_ClientReceiver.Receive(ref parent.ipEndPoint); 
        parent.ThreadOutput(receiveBytes); 
       } 
       catch (Exception e) 
       { 
        parent.StatusUpdate(e.ToString()); 
       }       
     }   
    } 

    public void ThreadOutput(Byte[] message) 
    { 
     m_MainCallback(message);   
    } 

public partial class SystemMain : Form 
{ 
    //Local Class Variables 
    Network.ReceiveEngineCS SystemMessageReceiver; 
    private void Form1_Load(object sender, EventArgs e) 
    { 
     //Load up the message receiver 
     SystemMessageReceiver = new Network.ReceiveEngineCS(localAddy, fromAddy, new mydelegate(LocalDelegate)); 
    } 

    public void LocalDelegate(Byte[] message) 
    { 
     if (Form.ListView.InvokeRequired) 
     { 
      //External call: invoke delegate 
      Form.ListView.Invoke(new mydelegate(this.LocalDelegate), message); 
     } 
     else 
     { 
      //Get the Packet Header 
      Formats.PacketHeaderObject ph = new Formats.PacketHeaderObject(message); 
      //Update or Add item to Specific ListView 
      ... update views 
     } 
    } 
} 

Receiver每秒接收10到100個實時消息,並且通常更多。

我一直在做研究最近到.NET 4.0和C#,發現許多其他類似的方式來做到這一點的數據處理 ,比如工作線程,並使用代表和調用的其他方式。

我的問題...有沒有在新的.NET庫(3.5,4.0等)更有效的方式這個數據接收/ GUI更新呢?

我覺得這個方法不使用C#的工作,以及。

任何幫助,將不勝感激。

+1

每秒向用戶投擲100個通知沒有多大意義。它看起來像模糊。先讓你的UI可用,也解決你的問題。 Winforms沒有變化。 – 2011-04-25 00:18:48

+0

好主意漢斯。如果我在線程中構建關於傳入數據的元數據,並且每秒鐘只更新gui,那麼它仍然會給予用戶有關數據的體面迴應,同時允許Gui不會因更新而停滯不前。通常操作員使用更新數據項的粗略計數作爲數據速率的指導。因此,一眼就可以計算出十秒一秒50等等。但我可以將它作爲元更新並將其添加到列表視圖中。 – Sleff 2011-04-25 02:53:47

沙发
0
4

一個更新發布到GUI的最好的方法是讓工作線程包了包含在更新的數據,並將其放置在隊列中。 UI線程將定期輪詢隊列。有工作者線程使用Control.Invoke更新UI是方式在我看來過度使用。相比之下,讓UI線程輪詢更新有幾個優點。

  • 它打破了用戶界面和工作者線程之間的緊密耦合,這些線程與Control.Invoke強加。
  • 它將UI線程更新的責任置於UI線程上,無論如何它應該屬於它。
  • UI線程可以決定更新應該發生的時間和頻率。
  • 不存在UI消息泵被超載的風險,這與工作線程啓動的封送處理技術的情況相同。
  • 工作線程不必等待確認該更新與它的下一個步驟之前執行(即你同時在用戶界面和工作線程的詳細吞吐量)。

你沒有提到ThreadOutput是如何實現的,但你可以考慮我上面提到的,如果它尚未做到這一點的方式方法。經驗告訴我,這通常是最好的方法。讓UI線程節制其更新週期是一大優勢。

+0

我一直用共享資源輪詢數據的問題一直是時機之一。鎖定和解鎖共享隊列會導致大量開銷(當然,我不會遇到這個問題)。我查看了工作線程,發現有線程發送更新的機制。我不確定是否必須使用調用工作線程來更新擁有UI的隊列? – Sleff 2011-04-25 03:05:02

+0

與'Control.Invoke'相比,隊列的鎖定是一件非常昂貴的操作。另外,你總是可以使用'ConcurrentQueue',它使用無鎖機制。工作線程不必使用'Invoke'將某些東西發佈到隊列中。首先將其稱爲「UI擁有隊列」可能有點誤導。 – 2011-04-25 12:54:35

+0

這有助於@布萊恩。我不確定調用的費用。我一直看到它用於從簡單文件或低速率xml數據更新gui的簡單示例中。我將檢查ConcurrentQueue,並考慮減少@Hans建議的需要更新的內容。 – Sleff 2011-04-25 16:27:40

板凳
0
1

看看到Thread Pooling而不是每次紡紗新的線程。

+0

謝謝贊博尼,但我實際上只使用一個線程的消息接收應用程序的整個生命週期。這些消息是我需要始終接收的事件的連續更新。 – Sleff 2011-04-25 02:46:39

495
votes
answers
13 views
+10

when & why to use delegates? [duplicate]

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?

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

沙发
+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.