.NETのデリゲヌトずむベント

翻蚳者から。 私自身の経隓から、そしお仲間のプログラマヌの経隓から刀断するず、C  蚀語 ず.NETプラットフォヌムの すべおの基本的な機胜の 䞭で、デリゲヌトずむベントは最も耇雑なものの1぀である ず蚀え たす。 おそらくこれは、䞀芋したずころデリゲヌトずむベントの必芁性が明癜でないように思われるずいう事実、たたは甚語の混乱のためです。 そこで、私はゞョン・スキヌトによる、最も基本的なレベルである「指で」のデリゲヌトずむベントに関する蚘事を翻蚳するこずにしたした。 C  /.NETに 粟通しおいるが 、デリゲヌトずむベントを理解するのが難しい 人に理想的です 。

ここで玹介する翻蚳は無料です。 ただし、原則ずしお「無料」が省略、簡略化、蚀い換えを含む省略された翻蚳ずしお理解されおいる堎合は、すべおが逆になりたす。 この翻蚳は、オリゞナルのわずかに拡匵、改良、曎新されたバヌゞョンです。 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 + d1d1
d1 + nulld1
d1 + d2[d1、d2]
d1 + [d2、d3][d1、d2、d3]
[d1、d2] + [d2、d3][d1、d2、d2、d3]
[d1、d2]-d1d2
[d1、d2]-d2d1
[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 . ? :
  1. () .
  2. () .
  3. () 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- .

Source: https://habr.com/ru/post/J198694/


All Articles