ここで紹介する翻訳は無料です。 ただし、原則として「無料」が省略、簡略化、言い換えを含む省略された翻訳として理解されている場合は、すべてが逆になります。 この翻訳は、オリジナルのわずかに拡張、改良、更新されたバージョンです。 Sergey Teplyakov別名SergeyTに感謝します 。彼はこの記事の翻訳とデザインに計り知れない貢献をしてくれました。
多くの場合、イベントとデリゲートの違いを理解するのは困難です。 また、C#は、同じ名前のデリゲート変数に自動的に変換されるフィールドのようなイベントを宣言できるため、状況をさらに混乱させます。 この記事は、この問題を明確にすることを目的としています。 もう1つのポイントは、「デリゲート」という用語との混同です。これにはいくつかの意味があります。 デリゲートタイプを指定するために使用されることもあれば、デリゲートインスタンスを指定するために使用されることもあります。 混乱を避けるために、デリゲートのタイプとデリゲートのインスタンス、および「デリゲート」という単語を使用する場合は、これらの用語を明示的に使用します。つまり、最も広い意味でそれらについて話します。
デリゲートの種類
ある意味では、デリゲート型は、明確に定義された署名を持つメソッドが1つだけあるインターフェイスと考えることができます(この記事では、メソッドの署名をそのすべての入力(出力および参照)パラメーターとして理解し、戻り値)。 デリゲートインスタンスは、このインターフェイスを実装するオブジェクトです。 この意味で、デリゲートインスタンスを使用すると、「インターフェイス」で定義されたメソッドのシグネチャとシグネチャが一致する既存のメソッドを呼び出すことができます。 デリゲートには他の機能もありますが、定義済みの署名を使用してメソッドを呼び出す機能はデリゲートの本質です。 デリゲートインスタンスは、ターゲットメソッドへの参照(ポインター、ラベル)を格納し、このメソッドがインスタンスの場合、ターゲットメソッドが配置されているオブジェクト(クラスまたは構造体)のインスタンスへの参照を格納します。
デリゲートタイプは、
delegate
      
      キーワードを使用して宣言されます。 デリゲート型は、独立したエンティティとして存在するか、クラスまたは構造内で宣言できます。 例:
 namespace DelegateArticle { public delegate string FirstDelegate (int x); public class Sample { public delegate void SecondDelegate (char a, char b); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      この例では、2種類のデリゲートが宣言されています。 1つ目は
DelegateArticle.FirstDelegate
      
      で、名前空間レベルで宣言されます。 これは、
int
      
      型の1つのパラメーターを持ち、
string
      
      型の値を返すメソッドと「互換性があります」。 2番目は
DelegateArticle.Sample.SecondDelegate
      
      、これはクラス内で既に宣言されており、そのメンバーです。 戻り型は
void
      
      としてマークされているため、
char
      
      型の2つのパラメーターを持ち、何も返さないメソッドと「互換性があります」。
両方のタイプのデリゲートには、
public
      
      アクセス
public
      
      ます。 一般に、アクセス修飾子に関して、デリゲート型はクラスおよび構造と同じように動作します。 アクセス修飾子がデリゲートタイプに対して明示的に指定されておらず、このタイプがネームスペース内で宣言されている場合、このネームスペース内にあるすべてのオブジェクトで使用できます。 修飾子なしのデリゲート型がクラスまたは構造内で宣言されている場合、
private
      
      修飾子のアクションと同様に閉じられます。
デリゲート型を宣言する場合、
static
      
      修飾子を使用できません。
ただし、
delegate
      
      キーワードは必ずしもデリゲート型宣言を意味するわけではないことに注意してください。 匿名メソッドを使用してデリゲートインスタンスを作成する場合、同じキーワードが使用されます。
この例で宣言されたデリゲートの両方のタイプは、
System.MulticastDelegate
      
      から継承し、
System.MulticastDelegate
      
      は
System.Delegate
      
      から継承します。 実際には、
MulticastDelegate
      
      からの継承のみを考慮してください
Delegate
      
      と
MulticastDelegate
      
      違いは、主に歴史的な側面にあります。 これらの違いは、.NET 1.0のベータバージョンでは重要でしたが、不便であったため、Microsoftは2つのタイプを1つにまとめることにしました。 残念ながら、決定は遅すぎました。そして、決定が下されたとき、彼らは.NETの基盤に影響を与えるこのような重大な変更を敢行しませんでした。 したがって、
Delegate
      
      と
MulticastDelegate
      
      は同一のものであると考えてください。
作成するデリゲートの各タイプは、
MulticastDelegate
      
      からメンバーを継承します。つまり、
Object
      
      パラメーターと
IntPtr
      
      パラメーターを持つ1つのコンストラクターと、
Invoke
      
      、
BeginInvoke
      
      、
EndInvoke
      
      3つのメソッドです。 あとでデザイナーに戻ります。 実際、これらの3つのメソッドは、デリゲートの各タイプのシグネチャが異なるため、文字通り継承されません-宣言されたデリゲートタイプのメソッドのシグネチャに「適応」します。 上記のコード例を見て、
FirstDelegate
      
      デリゲートの最初のタイプの「継承された」メソッドを派生させます。
 public string Invoke (int x); public System.IAsyncResult BeginInvoke(int x, System.AsyncCallback callback, object state); public string EndInvoke(IAsyncResult result);
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ご覧のとおり、
Invoke
      
      EndInvoke
      
      と
EndInvoke
      
      の戻り値の型は、
Invoke
      
      メソッドパラメーターと最初の
BeginInvoke
      
      パラメーターのように、デリゲートシグネチャで指定されたものと一致します。
Invoke
      
      メソッドの目的はこの記事の後半で、
BeginInvoke
      
      と
EndInvoke
      
      はデリゲートの高度な使用法を説明するセクションで説明します。 デリゲートインスタンスを作成する方法すら知らないため、これについて話すのは時期尚早です。 これについては次のセクションで説明します。
デリゲートインスタンス:基本
デリゲート型がどのように宣言され、何が含まれているかがわかったので、デリゲートのインスタンスを作成する方法と、それで何ができるかを見てみましょう。
デリゲートインスタンスの作成
まず、この記事では、C#4.0に登場した一般化されたデリゲートをカバーしていないのと同様に、デリゲートインスタンスの作成に関連するC#2.0および3.0の新機能については触れていません。 クロージャーについての別の記事The Beauty of Closuresでは、C#2.0および3.0に登場した新しいデリゲート機能について説明しています。 さらに、このトピックに関する多くの情報は、私の著書「 C#in Depth 」の第5章、第9章、および第13章に含まれています。 C#1.0 / 1.1に登場した明示的なデリゲートのインスタンス化スタイルを順守します。これは、このようなスタイルの方が「フードの下で」何が起こっているかを理解しやすいと思うからです。 これで、基本を理解したら、C#2.0、3.0、および4.0の新機能を学習できます。 逆もまた同様です。この記事に記載されている基本事項をしっかり理解していないと、デリゲートの「新しい」機能は耐えられないかもしれません。
前述のように、デリゲートの各インスタンスには、必然的に、デリゲートのこのインスタンスを介して呼び出すことができるターゲットメソッドへの参照と、ターゲットメソッドが宣言されているオブジェクト(クラスまたは構造体)のインスタンスへの参照が含まれます。 ターゲットメソッドが静的である場合、もちろんインスタンスへの参照はありません。 CLRは、静的メソッドに渡される最初の引数がデリゲートインスタンスに格納されるか、メソッドが呼び出されるときにターゲットインスタンスメソッドへの参照が引数として渡される、他のわずかに異なる形式のデリゲートもサポートします。 詳細については、MSDNの
System.Delegate
      
      ドキュメントを参照してください。ただし、この段階では、この追加情報は重要ではありません。
したがって、インスタンスを作成するには、2つの「ユニット」のデータ(もちろん、デリゲート自体のタイプ)が必要であることを知っていますが、どのようにコンパイラに知らせるのですか? C#仕様で「 delegate-creation-expression 」と呼ばれるものを使用します。これは、 新しいdelegate-type(expression)の形式です。 式は、同じ型の別のデリゲート(またはC#2.0の互換性のあるデリゲート型)、またはオブジェクトのインスタンスへのメソッド名とオプションの参照で構成される「メソッドグループ」である必要があります。 メソッドのグループは、通常のメソッド呼び出しと同じように指定されますが、引数と括弧はありません。 デリゲートコピーを作成する必要はほとんどないため、より一般的なフォームに焦点を当てます。 以下に例を示します。
 /*      d1  d2 .  InstanceMethod   ,    ,       ( ). ,     — this,      . */ FirstDelegate d1 = new FirstDelegate(InstanceMethod); FirstDelegate d2 = new FirstDelegate(this.InstanceMethod); /*  (d3)    ,     ,      ,        . */ FirstDelegate d3 = new FirstDelegate(anotherInstance.InstanceMethod); /*   (d4)      ,  ,     ;        . */ FirstDelegate d4 = new FirstDelegate(instanceOfOtherClass.OtherInstanceMethod); /*    (d5)     ,      ,     ( ). */ FirstDelegate d5 = new FirstDelegate(StaticMethod); /*  (d6)      ,       . */ FirstDelegate d6 = new FirstDelegate(OtherClass.OtherStaticMethod);
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      前に説明したデリゲートコンストラクターには、
System.IntPtr
      
      型のメソッドへの参照(MSDNドキュメントではこのパラメーターはmethodと呼ばれます)と
System.Object
      
      型のオブジェクトのインスタンスへのリンク(MSDNドキュメンテーションではこのパラメーターはtargetと呼ばれます) methodパラメーターで指定されたメソッドが静的な場合、nullを受け入れます。
重要な点:デリゲートインスタンスは、デリゲートインスタンスが呼び出されるコード内の場所に関して(スコープ外で)見えないオブジェクトのメソッドとインスタンスを参照できます。 たとえば、デリゲートインスタンスを作成する場合、プライベートメソッドを使用し、このデリゲートインスタンスを別のパブリックメソッドまたはプロパティから返すことができます。 一方、デリゲートインスタンスの作成時に指定されたオブジェクトのインスタンスは、呼び出されたときに、呼び出しが行われたオブジェクトに関して不明になるオブジェクトにすることができます。 重要なことは、デリゲートがインスタンス化される時点で、メソッドとオブジェクトのインスタンスの両方が(スコープ内で)利用可能でなければならないことです。 つまり、コード内で特定のオブジェクトのインスタンスを作成し、このインスタンスから特定のメソッドを呼び出すことができる場合(のみ)、このメソッドとオブジェクトのインスタンスを使用して、デリゲートのインスタンスを作成できます。 ただし、以前に作成したデリゲートのインスタンスへの呼び出し中は、アクセス権とスコープは無視されます。 課題といえば...
デリゲートインスタンスの呼び出し
デリゲートインスタンスは、通常のメソッドが呼び出されるのと同じ方法で呼び出されます。 たとえば、
delegate string FirstDelegate (int x)
      
      として最上部でタイプが定義されているデリゲートインスタンスd1を呼び出すと、次のようになります。
 string result = d1(10);
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      デリゲートインスタンスによって参照されるメソッドは、オブジェクトインスタンス(ある場合)と「within」(または「in context」)と呼ばれ、結果が返されます。 デリゲートの作業を示す本格的なプログラムを作成すると同時に、「余分な」コードを含まないコンパクトなプログラムを作成することは簡単な作業ではありません。 ただし、以下は1つの静的メソッドと1つのインスタンスメソッドを含む同様のプログラムです。
DelegateTest.StaticMethod
      
      を呼び出すことは、
StaticMethod
      
      を呼び出すこと
DelegateTest.StaticMethod
      
      同等です。例をわかりやすくするために、クラス名を含めました。
 using System; public delegate string FirstDelegate (int x); class DelegateTest { string name; static void Main() { FirstDelegate d1 = new FirstDelegate(DelegateTest.StaticMethod); DelegateTest instance = new DelegateTest(); instance.name = "My instance"; FirstDelegate d2 = new FirstDelegate(instance.InstanceMethod); Console.WriteLine (d1(10)); //    "Static method: 10" Console.WriteLine (d2(5)); //    "My instance: 5" } static string StaticMethod (int i) { return string.Format ("Static method: {0}", i); } string InstanceMethod (int i) { return string.Format ("{0}: {1}", name, i); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      デリゲートインスタンスを呼び出すためのC#構文は、各デリゲートタイプが持つinvokeメソッド呼び出しをマスクする構文糖衣です。 デリゲートは、
BeginInvoke/EndInvoke
      
      を提供する場合、非同期で実行できますが、これについては後で詳しく説明します。
デリゲートの組み合わせ
デリゲートの1つのインスタンスを呼び出すと、メソッドのセット全体が呼び出され、これらのメソッドは異なるクラスの異なるインスタンスから取得できるように、デリゲートを結合(結合および減算)できます。 デリゲートインスタンスがメソッドとオブジェクトのインスタンスへの参照を格納すると以前に言ったとき、物事を少し単純化しました。 これは、同じメソッドを表すデリゲートインスタンスに当てはまります。 明確にするために、以降、このようなデリゲートのインスタンスを 「単純なデリゲート」(単純なデリゲート)と呼びます 。 対照的に、実際には単純なデリゲートのリストであるデリゲートインスタンスがあり、それらはすべて同じタイプのデリゲートに基づいています(つまり、参照されているメソッドの署名が同じです)。 このようなデリゲートのインスタンスを 「結合デリゲート」(結合デリゲート)と呼びます 。 結合された複数のデリゲートを互いに組み合わせて、単純なデリゲートの1つの大きなリストにできます。 結合されたデリゲート内の単純なデリゲートのリストは、「呼び出しリスト」または「呼び出しリスト」と呼ばれます。 したがって、呼び出しリストは、呼び出しの順序で配置されたオブジェクトのメソッドとインスタンスへのリンクのペアのリストです。
デリゲートインスタンスは常に不変であることを知っておくことが重要です。 デリゲートのインスタンスを結合するたびに(および減算する場合-以下でこれを検討します)、新しい結合デリゲートが作成されます。 文字列の場合とまったく同じです
String.PadLeft
      
      を文字列のインスタンスに適用すると、メソッドはこのインスタンスを変更しませんが、変更が加えられた新しいインスタンスを返します。
2つのデリゲートインスタンス間の結合(「加算」という用語も発生します)は、通常、デリゲートインスタンスが数字または文字列であるかのように、加算演算子を使用して行われます。 同様に、デリゲートの1つのインスタンスを別のインスタンスから減算(「削除」という用語も発生)するには、減算演算子を使用します。 1つの結合されたデリゲートを別のデリゲートから減算する場合、減算は呼び出しリストの一部として実行されることに注意してください。 元の(削減された)呼び出しリストに、控除可能な呼び出しリストに含まれている単純なデリゲートが1つもない場合、結果(差)は元のリストになります。 それ以外の場合、元のリストに単純なデリゲートが含まれていて、それも減算されたデリゲートに存在する場合、単純なデリゲートの最後の出現のみが結果のリストに存在しません。 ただし、これは言葉で説明するよりも例で示す方が簡単です。 しかし、次のソースコードの代わりに、次の表の例を使用して結合と減算の操作を示します。 その中で、リテラルd1、d2、d3は単純なデリゲートを示します。 さらに、[d1、d2、d3]という指定は、この順序で3つの単純なデリゲートで構成される複合デリゲートを意味します。つまり、 呼び出されると、d1が最初に呼び出され、次にd2、次にd3が呼び出されます。 空のコールリストはnullです。
| 表現 | 結果 | 
|---|---|
| null + d1 | d1 | 
| d1 + null | d1 | 
| d1 + d2 | [d1、d2] | 
| d1 + [d2、d3] | [d1、d2、d3] | 
| [d1、d2] + [d2、d3] | [d1、d2、d2、d3] | 
| [d1、d2]-d1 | d2 | 
| [d1、d2]-d2 | d1 | 
| [d1、d2、d1]-d1 | [d1、d2] | 
| [d1、d2、d3]-[d1、d2] | d3 | 
| [d1、d2、d3]-[d2、d1] | [d1、d2、d3] | 
| [d1、d2、d3、d1、d2]-[d1、d2] | [d1、d2、d3] | 
| [d1、d2]-[d1、d2] | ヌル | 
加算演算子に加えて、静的な
Delegate.Combine
      
      メソッドを使用してデリゲートインスタンスを結合できます。 同様に、減算操作には、静的メソッド
Delegate.Remove
      
      形式の代替手段があります。 一般的に、加算演算子と減算演算子は一種の構文上の砂糖であり、C#コンパイラーはコードでそれらを満たし、CombineメソッドとRemoveメソッドの呼び出しに置き換えます。 これらのメソッドは静的であるため、nullデリゲートを簡単に処理できます。
加算演算子と減算演算子は、常に代入演算
d1 += d2
      
      一部として機能します。これは、式
d1 = d1+d2
      
      と完全に同等です。 減算でも同じです。 繰り返しますが、加算と減算に関係するデリゲートのインスタンスは、操作中に変更されないことを思い出してください。 この例では、変数d1は、「古い」d1とd2で構成される新しく作成された結合デリゲートへのリンクを単に変更します。
デリゲートの追加と削除はリストの最後から行われるため、呼び出しシーケンスx + = y; x-= y; 空の操作に相当します(変数xにはサブスクライバーの不変リストが含まれます( 約transl ))。
デリゲート型シグネチャが値を返すように宣言されている(つまり、戻り値がvoidではない)場合、デリゲートの「結合された」インスタンスがこの型に基づいて作成され、呼び出されると、最後の単純なデリゲートによって「提供された」戻り値が変数に書き込まれます複合デリゲート呼び出しリスト。
結合されたデリゲート(多くの単純なデリゲートで構成される呼び出しリストを含む)があり、いくつかの単純なデリゲートで呼び出されたときに例外が発生すると、この時点で結合デリゲートの呼び出しが停止し、例外がスローされ、呼び出しリストから他のすべての単純なデリゲート呼び出されることはありません。
イベント
まず最初に:イベントはデリゲートインスタンスではありません。 そして今再び:
イベントはデリゲートインスタンスではありません。
ある意味では、C#が特定の状況で同じ方法でイベントとデリゲートインスタンスの使用を許可するのは残念ですが、違いを理解することは非常に重要です。
イベントを理解する最善の方法は、イベントを「似た」プロパティと考えることであるという結論に達しました。 プロパティは「似ている」フィールドに見えますが、実際にはそうではありません。フィールドをまったく使用しないプロパティを作成できます。 イベントは同様に振る舞います-加算および減算演算の点ではデリゲートインスタンスのように見えますが、実際にはそうではありません。
イベントは、IL(CIL、MSIL)で適切に「フレーム化」され、相互接続されるメソッドのペアです。これにより、言語環境は、「単純」メソッドではなく、イベントを表すメソッドで「処理」することを明確に認識します。 メソッドは、追加および削除操作に対応します。各操作は、イベントの型と同じ型を持つデリゲートインスタンスで1つのパラメーターを受け取ります。 これらの操作で行うことは主にあなた次第ですが、通常、追加操作と削除操作は、イベントハンドラーのリストへのデリゲートインスタンスの追加と削除に使用されます。 イベントが発生すると(そして、ボタンのクリック、タイマー、または未処理の例外など、何が発生したかは関係ありません)、ハンドラーが順番に呼び出されます(順番に)。 C#では、イベントハンドラーの呼び出しはイベント自体の一部ではないことに注意してください。
C#
eventName += delegateInstance;
      
      eventName -= delegateInstance;
      
      ,
eventName
      
      (,
myForm.Click
      
      ) (,
MyClass.SomeEvent
      
      ). , .
. — (explicit) add remove; (get) (set), event.
System.EventHandler
      
      . , add remove , — , . , , remove , , null.
 using System; class Test { public event EventHandler MyEvent //  MyEvent   EventHandler { add { Console.WriteLine ("add operation"); } remove { Console.WriteLine ("remove operation"); } } static void Main() { Test t = new Test(); t.MyEvent += new EventHandler (t.DoNothing); t.MyEvent -= null; } //-,        EventHandler void DoNothing (object sender, EventArgs e) { } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ,
value
      
      , . , , , , . , , , , , — . Windows Forms — .. , null.
Field-like
C# . «field-like » (field-like event) — , «» ( ), «» add remove.
 public event EventHandler MyEvent;
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      . (.., ), . (implicit) / , (lock). C# 1.1
MyEvent
      
      :
 private EventHandler _myEvent; public event EventHandler MyEvent { add { lock (this) { _myEvent += value; } } remove { lock (this) { _myEvent -= value; } } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      これは、インスタンスメンバー用です。静的イベントに関しては、変数も静的であり、ビュータイプでロックがキャプチャされます
typeof(XXX)
      
      , XXX — , . C# 2.0 , . , , , — , . ( , , , . ; , .) , , . .
, ,
MyEvent
      
      ? ( ) , (
_myEvent
      
      ). , .
ポイントは何ですか?
, , , : , ? — - . , C#/.NET . ? :
- () .
- () .
- () AddXXXHandler RemoveXXXHandler.
№1 — , , . №2 , (override) —
someInstance.MyEvent = eventHandler;
      
      ,
eventHandler
      
      , ,
eventHandler
      
      . , .
№3 — , , , , ( IL) «» , field-like . / , .
(: C# 4 . : . « »)
(locking), field-like add remove, . (thread safety). , . , C# 2.0 this- . , (deadlock).
, — - , C# 2.0 , , , , , () . - , , 1↓ .
, , , , , , , add/remove , «» add/remove . :
 /// <summary> ///    SomeEventHandler,  «» . /// </summary> SomeEventHandler someEvent; /// <summary> ///       SomeEvent. /// </summary> readonly object someEventLock = new object(); /// <summary> ///   /// </summary> public event SomeEventHandler SomeEvent { add { lock (someEventLock) { someEvent += value; } } remove { lock (someEventLock) { someEvent -= value; } } } /// <summary> ///   SomeEvent /// </summary> protected virtual void OnSomeEvent(EventArgs e) { SomeEventHandler handler; lock (someEventLock) { handler = someEvent; } if (handler != null) { handler (this, e); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , - — . , «» ( , ), null : , . , , , - , add/remove , .
,
handler
      
      someEvent, handler ,
someEvent
      
      . , ,
someEvent
      
      null,
handler
      
      , , . , (immutable), , (
handler = someEvent
      
      ) (
handler (this, e);
      
      ), .
, , . ? ? , «». , , , . , add/remove, ; , , C# «» «» . . — , .
 /// <summary> ///    SomeEventHandler,  «» . /// </summary> SomeEventHandler someEvent; /// <summary> ///   /// </summary> public event SomeEventHandler SomeEvent { add { someEvent += value; } remove { someEvent -= value; } } /// <summary> ///   SomeEvent /// </summary> protected virtual void OnSomeEvent(EventArgs e) { if (someEvent != null) { someEvent (this, e); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      OnSomeEvent
      
      someEvent
      
      ( , add remove), null, , null. . - (no-op), « » .
OnSomeEvent
      
      . «» , - .
:
,
someDelegate(10)
      
      —
someDelegate.Invoke(10)
      
      .
Invoke
      
      ,
BeginInvoke
      
      /
EndInvoke
      
      . CLI , C# . , .NET, (callback handler) , . , (thread-pool) .NET.
, 2↓ , ,
BeginInvoke
      
      EndInvoke
      
      . , , , . , , , «» , .
EndInvoke
      
      , . ,
EndInvoke
      
      .
 using System; using System.Threading; delegate int SampleDelegate(string data); class AsyncDelegateExample1 { static void Main() { SampleDelegate counter = new SampleDelegate(CountCharacters); SampleDelegate parser = new SampleDelegate(Parse); IAsyncResult counterResult = counter.BeginInvoke("hello", null, null); IAsyncResult parserResult = parser.BeginInvoke("10", null, null); Console.WriteLine("   ID = {0}  ", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("  '{0}'", counter.EndInvoke(counterResult)); Console.WriteLine("  '{0}'", parser.EndInvoke(parserResult)); Console.WriteLine("   ID = {0} ", Thread.CurrentThread.ManagedThreadId); } static int CountCharacters(string text) { Thread.Sleep(2000); Console.WriteLine("    '{0}'    ID = {1}", text, Thread.CurrentThread.ManagedThreadId); return text.Length; } static int Parse(string text) { Thread.Sleep(100); Console.WriteLine("  '{0}'    ID = {1}", text, Thread.CurrentThread.ManagedThreadId); return int.Parse(text); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      Thread.Sleep
      
      , ,
CountCharacters
      
      Parse
      
      .
CountCharacters
      
      2 , — , , ( ). «» , «», . :
ID = 9 '10' ID = 10 'hello' ID = 6 '5' '10' ID = 9
,
EndInvoke
      
      Thread.Join
      
      — , .
IAsyncResult
      
      ,
BeginInvoke
      
      EndInvoke
      
      ,
BeginInvoke
      
      ( —
Object state
      
      ), .
, , , (callback model), . , (callback)
EndInvoke
      
      , . ( ), , , . ,
BeginInvoke
      
      . , , , .
state
      
      Object
      
      , ,
DisplayResult
      
      .
IAsyncResult
      
      AsyncResult
      
      : , ,
AsyncResult
      
      , , ,
EndInvoke
      
      . ,
AsyncResult
      
      System.Runtime.Remoting.Messaging
      
      ( ),
System
      
      System.Threading
      
      .
 using System; using System.Threading; using System.Runtime.Remoting.Messaging; delegate int SampleDelegate(string data); class AsyncDelegateExample2 { static void Main() { SampleDelegate counter = new SampleDelegate(CountCharacters); SampleDelegate parser = new SampleDelegate(Parse); AsyncCallback callback = new AsyncCallback(DisplayResult); counter.BeginInvoke("hello", callback, "  '{0}'    ID = {1}"); parser.BeginInvoke("10", callback, "  '{0}'    ID = {1}"); Console.WriteLine("   ID = {0}  ", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); Console.WriteLine("   ID = {0} ", Thread.CurrentThread.ManagedThreadId); } static void DisplayResult(IAsyncResult result) { string format = (string)result.AsyncState; AsyncResult delegateResult = (AsyncResult)result; SampleDelegate delegateInstance = (SampleDelegate)delegateResult.AsyncDelegate; Int32 methodResult = delegateInstance.EndInvoke(result); Console.WriteLine(format, methodResult, Thread.CurrentThread.ManagedThreadId); } static int CountCharacters(string text) { Thread.Sleep(2000); Console.WriteLine("    '{0}'    ID = {1}", text, Thread.CurrentThread.ManagedThreadId); return text.Length; } static int Parse(string text) { Thread.Sleep(100); Console.WriteLine("  '{0}'    ID = {1}", text, Thread.CurrentThread.ManagedThreadId); return int.Parse(text); } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      . «» , . (background) , «» (.. ), , ,
Thread.Sleep(3000)
      
      — , 3- . ,
Thread.Sleep(3000)
      
      — .
. — ,
EndInvoke
      
      . (100 ), (2 ), , , .
ID = 9 '10' ID = 11 '10' ID = 11 'hello' ID = 10 '5' ID = 10 ID = 9
,
EndInvoke
      
      ; , .
EndInvoke
      
      , . « Multi-threading in .NET: Introduction and suggestions », , , « The Thread Pool and Asynchronous Methods ».
おわりに
. , , .
注釈
1. . perev。 , . , — , , «CLR via C#» , 4 . 2006 , 2007 , « 5. CLR» – « 24. » — « «» ».
2. . perev。 , , . . , , , .
翻訳者から
, , , . , , , . , , .
. (RSDN).
( 2006 ) , « » : MulticastDelegate, , MSIL, EventHandlerList, . , , .
coffeecupwinner . .NET .
, « »? C# 4 field-like , , : Interlocked.CompareExchange, . , , . , , , .
Daniel Grunwald. andreycha . C# .
C#/.NET C++ , , . : , .
rroyter . C#?
, , . , . , , C# 2- 3- .