11

[編集、完全に言い換え:] 私の質問は確かに言葉遣いが不十分で、受け入れも不十分だったようです。したがって、この完全な言い換えが役立つことを願っています...

Control.BeginInvoke()コントロールのハンドルが作成されたスレッドでデリゲートを実行します。通常、これは GUI スレッドです。 Dispatcher.BeginInvoke()は、Dispatcher オブジェクトが作成されたスレッドで実行されます。これは私が作成した任意のスレッドになります。

ただし、デリゲートの場合、「CLR はBeginInvoke と EndInvoke を自動的に定義し」、これらの呼び出しは代わりに ThreadPool スレッドで実行されます。この少し驚くべき異なる動作は別として、自動的に実装されるすべての関数の仕様をどのように見つけることができるのだろうかと思います。

例: Intelli センスは、私のデリゲートに DynamicInvoke() があることを示しています。クラスSystem.Delegate{}には DynamicInvoke() があり、デリゲートがそれを継承していることを示唆している可能性があります。しかし、Delegate{} には BeginInvoke() がありません。また、Delegate{} には、私のデリゲートにはない関数がいくつかあります。また、デリゲートは GetObjectData() メソッドを取得します。そして、これは ISerializable から来ているようです。

結論として、デリゲートは (1) CLR から「自動的に」、(2) Delegate{} の一部のサブセット、場合によっては MulticastDelegate{}、および場合によっては (3) ISerializble からメソッドを取得するように見えます。デリゲートが取得するすべてのメソッドの包括的な仕様はどこにありますか? 特に興味深いのは BeginInvoke() です。これは正確なシグネチャです。その名前を持つ前述の 2 つのメソッドは、異なるシグネチャ セットを持っています。

[誰かが編集で「デリゲート」は「デリゲート」であると提案しました。敢えて言えば、そうではありません。]

ありがとう

4

2 に答える 2

21

Control.Begin/End/Invoke() および Dispatcher.Begin/End/Invoke() メソッドは、デリゲートの Begin/End/Invoke() メソッドと同じ名前で動作が多少似ていますが、それらが同じだ。最も重要な違いは、デリゲートのメソッドがtype-safeであることです。これは、Control および Dispatcher バージョンには完全に欠けているものです。実行時の動作も大きく異なります。

デリゲートを管理するルールは、CLI 仕様、ECMA 335、章 II.14.6 で詳しく説明されています。章を読むのが最善です。概要を説明します。

デリゲート宣言は、MulticastDelegate から継承するクラスに変換されます (CLI 仕様で指定されているデリゲートではありません)。そのクラスには常に正確に 4 つのメンバーがあり、それらのランタイム実装は CLR によって提供されます。

  • オブジェクトと IntPtr を取るコンストラクタ。オブジェクトは Delegate.Target で、IntPtr はターゲット メソッド Delegate.Method のアドレスです。これらのメンバーは、後でデリゲートを呼び出すときに使用されます。デリゲートがバインドされているメソッドがインスタンス メソッドの場合、Target プロパティはthis参照を提供し、静的メソッドの場合は null を提供します。Method プロパティは、呼び出されるメソッドを決定します。これらの引数は直接指定しません。コンパイラは、new 演算子を使用するか、+= 演算子でイベント ハンドラーをサブスクライブするときにそれらを提供します。イベントの場合は多くのシンタックス シュガーがあるため、明示的にnew演算子を使用する必要はありません。

  • Invoke() メソッド。メソッドの引数は動的に生成され、デリゲート宣言と一致します。Invoke() メソッドを呼び出すと、デリゲート ターゲット メソッドが同じスレッド (同期呼び出し) で実行されます。C# で使用することはめったにありません。オブジェクト名とそれに続く括弧を使用するだけでデリゲート オブジェクトを呼び出すことができる構文シュガーを使用するだけです。

  • BeginInvoke() メソッドは、非同期呼び出しを行う方法を提供します。ThreadPool.QueueUserWorkItem に似ていますが、タイプ セーフな引数を使用して、ターゲット メソッドがビジー状態で実行されている間、メソッドはすばやく完了します。戻り値の型は常に System.IAsyncResult であり、非同期呼び出しがいつ完了して EndInvoke() メソッドに渡されるかを調べるために使用されます。最初の引数はオプションの System.AsyncCallback デリゲート オブジェクトです。非同期呼び出しが完了すると、そのターゲットが自動的に呼び出されます。2 番目の引数はオプションのオブジェクトで、そのままコールバックに渡され、状態を追跡するのに役立ちます。追加の引数は動的に生成され、デリゲート宣言と一致します。

  • EndInvoke() メソッド。IAsyncResult 型の引数を 1 つ取ります。BeginInvoke() から取得したものを渡す必要があります。非同期呼び出しを完了し、リソースを解放します。

デリゲート オブジェクトに表示される追加のメソッドは、基本クラスの MulticastDelegate および Delegate から継承されたものです。DynamicInvoke() や GetObjectData() と同様です。

非同期呼び出しは扱いにくいものであり、使用する必要はほとんどありません。実際には、Silverlight などの .NETCore ターゲットでは使用できません。デリゲート ターゲット メソッドは、Threadpool.QueueUserWorkItem() と同様に、任意のスレッド プール スレッドで実行されます。スローされる未処理の例外はすべてキャプチャされ、スレッドは終了しますが、プログラムは終了しません。EndInvoke() を呼び出す必要があります。これを行わないと、10 分間リソース リークが発生します。ターゲット メソッドが例外をスローした場合は、EndInvoke() を呼び出したときに再発生します。スレッドプールスレッドを制御することはできません。キャンセルまたは中止する方法はありません。Task クラスまたは Thread クラスは、より優れた代替手段です。

MSDN は関連していますが、デリゲート型のメソッドは文書化されていません。仕様とデリゲート宣言から、それらが何を行い、どのように見えるかを知っていることを前提としています。

于 2013-02-21T23:07:19.930 に答える
8

あなたの質問の主題によると、答えはこれらの太字になります。MSDNは良くないかもしれませんが、それでも良いです:)

Jeffrey Richter が、上記の質問であなたが尋ねたことについて書いています。彼は MSDN マガジンにこの記事を掲載しています。 http://msdn.microsoft.com/en-us/magazine/cc164139.aspx この記事では、BeginInvoke と EndInvoke が .NET で実際にどのように実装されているか (実際にはそうではないかもしれませんが、非常に近い) の実装について説明します。 CLR。この記事に時間を投資してください。その後は、先を読む必要はないと思います。 Jeffrey richter も、彼の著書 CLR Via C# の中で、これについて非常によく説明しています。

ほとんどの UI アプリケーションはシングル スレッドです。UI 上のコントロールには、それらが作成されたスレッドを使用してのみアクセスできます。

これを達成するために Control.Invoke が Winforms に存在します。UI スレッドでコードを自動的に呼び出します。WPF の世界では、Control.Invoke はありません。WPF には、Control の代わりに Dispatcher があります。

今デリゲート対デリゲート。 Hans Passant は非常に素晴らしい回答を提供してくれました。

ですから、少し詳しく説明するために、この回答を書いています。

MSDN で言及されているデリゲートはクラスです。このコードを見てみましょう (msdn http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspxから取得)

public delegate int PerformCalculation(int x, int y);

ここでわかるように、デリゲートがあります (小さな 'd' が付いていることに注意してください)。これは、デリゲートを定義するためのキーワード、または簡単な言葉で言えば、メソッドへの参照を実際に含む変数 PerformCalculation を定義するためのキーワードです。

あなたはすでにこれを認識していると思いますが、完全を期すために。

この変数を使用して、次のようなコードを使用してメソッドを呼び出します。

using System;
// Declare delegate -- defines required signature:
delegate void SampleDelegate(string message);

class TestDelegate
{
    private void CallMeUsingDelegate(string m_param)
    {
        Console.WriteLine("Called me using parameter - " + m_param);
    }

    public static void Main(string[] args)
    {
        // Here is the Code that uses the delegate defined above.
        SampleDelegate sd = new SampleDelegate(CallMeUsingDelegate);
        sd.Invoke("FromMain");
    }
}

メソッドを呼び出すには、上記の CallMeUsingDelegate メソッドとして完全なメソッドを記述する必要があります。C# には、実際にメソッドとして記述することなくメソッドを呼び出すために使用できるこの匿名メソッドがあります。

したがって、上記のコードは次のようにも記述できます。

システムを使用する; // デリゲートを宣言 -- 必要な署名を定義: delegate void SampleDelegate(string message);

class TestDelegate
{
    public static void Main(string[] args)
    {
        // Here is the Code that uses the delegate defined above.
        SampleDelegate sd = delegate(param) {
                        Console.WriteLine("Called me using parameter - " + param);
                    };

        sd.Invoke("FromMain");
    }
}

これは上記のコードと同じ働きをします。しかし、今はもう少し少ないコードを書く必要があります。コンパイラは、上記の両方のバージョンに対して同一の IL コードを作成します。ただし、2 の場合、新しいメソッドにはコンパイラによって自動生成された名前が付けられます。

BeginInvoke と EndInvoke に関しては、メソッドを非同期的に呼び出すために使用されます。これは、CLR で利用可能なスレッドプールを使用して行われます。

基本的に何が起こるかは、次を使用してメソッドを呼び出すことです

IAsyncResult ar = sd.BeginInvoke(CallMeUsingDelegate, callMeOnCompletion, sd);

ここで Delegate は、呼び出しているメソッドです。プログラムの Thread が BeginInvoke メソッドを呼び出し、CLR ThreadPool スレッドの Delegate パラメータで指定されたメソッドを内部的に呼び出します。次に、プログラムを実行し続け、IAsyncResult インターフェイスを実装するオブジェクトを返します。このオブジェクトを使用して、デリゲートを使用して呼び出されたタスクの進行状況についてクエリを実行できます (3 パラメーターとして渡されたデリゲート sd に注意してください)。

CallMeUsingDelegate メソッドは別のスレッド ( ThreadPool ) で呼び出されます。タスクが完了すると、ThreadPool は 2 パラメーターとして指定された Callback メソッドを呼び出します。

このすべてを見て、なぜ EndInvoke が必要なのかと思うかもしれません。

EndInvoke を呼び出さないと、CLR ThreadPool がこの操作への参照を保持し、メモリ リークが発生するためです。そのため、指定された Callback メソッドで EndInvoke を呼び出すことを常にお勧めします。

これで(すべてではありませんが)解決したことを願っていますが、いくつかの考えがあります。

于 2013-02-24T17:14:09.953 に答える