Metode anonim di Panggilan panggilan

131

Mengalami sedikit masalah dengan sintaksis tempat kami ingin memanggil delegasi secara anonim di dalam Control.Invoke.

Kami telah mencoba sejumlah pendekatan berbeda, semuanya sia-sia.

Sebagai contoh:

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

di mana someParameter bersifat lokal untuk metode ini

Di atas akan menghasilkan kesalahan kompiler:

Tidak dapat mengonversi metode anonim untuk mengetik 'System.Delegate' karena ini bukan tipe delegasi

Duncan
sumber

Jawaban:

221

Karena Invoke/ BeginInvokemenerima Delegate(daripada delegasi yang diketik), Anda perlu memberi tahu kompiler jenis delegasi yang akan dibuat; MethodInvoker(2.0) atau Action(3.5) adalah pilihan umum (perhatikan bahwa mereka memiliki tanda tangan yang sama); seperti itu:

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

Jika Anda perlu memasukkan parameter, maka "variabel yang ditangkap" adalah caranya:

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

(peringatan: Anda perlu sedikit berhati-hati jika menggunakan menangkap async , tetapi sinkronisasi baik-baik saja - yaitu di atas baik-baik saja)

Pilihan lain adalah menulis metode ekstensi:

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

kemudian:

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

Anda tentu saja dapat melakukan hal yang sama dengan BeginInvoke:

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

Jika Anda tidak dapat menggunakan C # 3.0, Anda bisa melakukan hal yang sama dengan metode instance biasa, mungkin di Formkelas dasar.

Marc Gravell
sumber
Bagaimana saya bisa meneruskan parameter ke solusi pertama Anda dalam jawaban ini? Saya maksudkan solusi ini: control.Invoke ((MethodInvoker) delegate {this.Text = "Hai";});
uzay95
1
Mengapa Metode Ekstensi dipanggil tanpa harus melakukan pemeran Eksplisit untuk Bertindak?
P.Brian.Mackey
Karena kompiler dapat menyimpulkan itu dari penggunaan.
RoboJ1M
1
Itu sama dengan bisa melakukan Form.Load += Loader()alih - alih yang lamaForm.Load += new EventHandler(Loader())
RoboJ1M
49

Sebenarnya Anda tidak perlu menggunakan kata kunci delegasi. Cukup berikan lambda sebagai parameter:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
Gunung Vokinneberg
sumber
16
myControl.Invoke(new MethodInvoker(delegate() {...}))
François
sumber
13

Anda perlu membuat jenis delegasi. Kata kunci 'delegate' dalam pembuatan metode anonim agak menyesatkan. Anda tidak membuat delegasi anonim tetapi metode anonim. Metode yang Anda buat dapat digunakan dalam delegasi. Seperti ini:

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

Demi kelengkapan, ini juga dapat dicapai melalui kombinasi metode Action / metode anonim:

//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();
});
mhamrah
sumber
Invoke((Action) Process);adalah jawaban terbaik, terima kasih!
Jinjinov
5

Saya memiliki masalah dengan saran lainnya karena saya terkadang ingin mengembalikan nilai dari metode saya. Jika Anda mencoba menggunakan MethodInvoker dengan nilai pengembalian, sepertinya tidak menyukainya. Jadi solusi yang saya gunakan adalah seperti ini (sangat senang mendengar cara untuk membuat ini lebih ringkas - Saya menggunakan 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;
        }));
    }
Rory
sumber
1

Saya suka menggunakan Action sebagai ganti MethodInvoker, lebih pendek dan terlihat lebih bersih.

Invoke((Action)(() => {
    DoSomething();
}));

// OR

Invoke((Action)delegate {
    DoSomething();
});

Misalnya.

// Thread-safe update on a form control
public void DisplayResult(string text){
    if (txtResult.InvokeRequired){
        txtResult.Invoke((Action)delegate {
            DisplayResult(text);
        });
        return;
    }

    txtResult.Text += text + "\r\n";
}
Du D.
sumber
0

Saya tidak pernah mengerti mengapa ini membuat perbedaan untuk kompiler, tetapi ini sudah cukup.

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

Bonus: tambahkan beberapa penanganan kesalahan, karena kemungkinan bahwa, jika Anda menggunakan Control.Invokedari utas latar belakang Anda memperbarui teks / progres / keadaan yang diaktifkan dari kontrol dan tidak peduli jika kontrol sudah dibuang.

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