web-dev-qa-db-ja.com

Invoke呼び出しの匿名メソッド

Control.Invoke内でデリゲートを匿名で呼び出す場合の構文に少し問題があります。

さまざまなアプローチを試してみましたが、すべて役に立ちませんでした。

例えば:

myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); }); 

someParameterはこのメソッドに対してローカルです

上記の結果、コンパイラエラーが発生します。

匿名メソッドはデリゲート型ではないため、「System.Delegate」型に変換できません

124
Duncan

Invoke/BeginInvokeは(型指定されたデリゲートではなく)Delegateを受け入れるため、作成するデリゲートのタイプをコンパイラに伝える必要があります。 MethodInvoker(2.0)またはAction(3.5)は一般的な選択肢です(同じ署名を持っていることに注意してください)。そのようです:

control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});

パラメータを渡す必要がある場合、「キャプチャされた変数」が道です。

string message = "Hi";
control.Invoke((MethodInvoker) delegate {this.Text = message;});

(注意:キャプチャasyncを使用する場合は少し注意する必要がありますが、syncは問題ありません-つまり、上記の問題はありません)

別のオプションは、拡張メソッドを書くことです:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate)action);
}

その後:

this.Invoke(delegate { this.Text = "hi"; });
// or since we are using C# 3.0
this.Invoke(() => { this.Text = "hi"; });

もちろんBeginInvokeでも同じことができます:

public static void BeginInvoke(this Control control, Action action)
{
    control.BeginInvoke((Delegate)action);
}

C#3.0を使用できない場合は、通常のインスタンスメソッド(おそらくFormベースクラス)でも同じことができます。

214
Marc Gravell

実際には、デリゲートキーワードを使用する必要はありません。ラムダをパラメーターとして渡すだけです:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
43
Vokinneberg
myControl.Invoke(new MethodInvoker(delegate() {...}))
14
François

デリゲートタイプを作成する必要があります。匿名メソッド作成のキーワード「デリゲート」は、少し誤解を招く恐れがあります。匿名デリゲートではなく、匿名メソッドを作成しています。作成したメソッドは、デリゲートで使用できます。このような:

myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
13
Jelon

完全を期すために、これはActionメソッド/匿名メソッドの組み合わせによっても実現できます。

//Process is a method, invoked as a method group
Dispatcher.Current.BeginInvoke((Action) Process);
//or use an anonymous method
Dispatcher.Current.BeginInvoke((Action)delegate => {
  SomeFunc();
  SomeOtherFunc();
});
6
mhamrah

メソッドから値を返すことがあるため、他の提案に問題がありました。 MethodInvokerを戻り値で使用しようとすると、気に入らないようです。したがって、私が使用するソリューションは次のようになります(これをより簡潔にする方法を聞いて非常に満足しています-私はc#.net 2.0を使用しています)。

    // Create delegates for the different return types needed.
    private delegate void VoidDelegate();
    private delegate Boolean ReturnBooleanDelegate();
    private delegate Hashtable ReturnHashtableDelegate();

    // Now use the delegates and the delegate() keyword to create 
    // an anonymous method as required

    // Here a case where there's no value returned:
    public void SetTitle(string title)
    {
        myWindow.Invoke(new VoidDelegate(delegate()
        {
            myWindow.Text = title;
        }));
    }

    // Here's an example of a value being returned
    public Hashtable CurrentlyLoadedDocs()
    {
        return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
        {
            return myWindow.CurrentlyLoadedDocs;
        }));
    }
5
Rory

なぜこれがコンパイラに違いをもたらすのか理解できませんでしたが、これで十分です。

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        control.Invoke(action);
    }
}

ボーナス:エラー処理を追加します。バックグラウンドスレッドからControl.Invokeを使用している場合、コントロールのテキスト/進行状況/有効化状態を更新し、コントロールが既に破棄されているかどうかは気にしない可能性が高いためです。

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
0