Phương thức ẩn danh trong cuộc gọi


131

Có một chút rắc rối với cú pháp mà chúng tôi muốn gọi một đại biểu ẩn danh trong Control.Invoke.

Chúng tôi đã thử một số cách tiếp cận khác nhau, tất cả đều vô ích.

Ví dụ:

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

Trong đó someParameter là cục bộ của phương thức này

Ở trên sẽ dẫn đến một lỗi trình biên dịch:

Không thể chuyển đổi phương thức ẩn danh sang loại 'System.Delegate' vì đây không phải là loại đại biểu

Câu trả lời:


221

Bởi vì Invoke/ BeginInvokechấp nhận Delegate(chứ không phải là một đại biểu đã gõ), bạn cần cho trình biên dịch biết loại đại biểu nào sẽ tạo; MethodInvoker(2.0) hoặc Action(3.5) là những lựa chọn phổ biến (lưu ý rằng chúng có cùng chữ ký); như vậy

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

Nếu bạn cần truyền tham số, thì "các biến được bắt" là cách:

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

(caveat: bạn cần phải có một chút thận trọng nếu sử dụng ảnh chụp async , nhưng đồng bộ là tốt - tức là ở trên là tốt)

Một tùy chọn khác là viết một phương thức mở rộng:

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

sau đó:

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

Tất nhiên bạn có thể làm tương tự với BeginInvoke:

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

Nếu bạn không thể sử dụng C # 3.0, bạn có thể làm tương tự với phương thức cá thể thông thường, có lẽ là trong Formlớp cơ sở.


Làm thế nào tôi có thể truyền tham số cho giải pháp đầu tiên của bạn trong câu trả lời này? Ý tôi là giải pháp này: control.Invoke ((MethodInvoker) ủy nhiệm {this.Text = "Hi";});
uzay95

1
Tại sao Phương thức mở rộng được gọi mà không cần phải thực hiện một diễn xuất rõ ràng để hành động?
P.Brian.Mackey

Bởi vì trình biên dịch có thể suy ra rằng từ việc sử dụng.
RoboJ1M

1
Nó giống như có thể làm Form.Load += Loader()thay vì cũForm.Load += new EventHandler(Loader())
RoboJ1M

49

Thật ra bạn không cần sử dụng từ khóa đại biểu. Chỉ cần vượt qua lambda là tham số:

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


13

Bạn cần tạo một loại đại biểu. Từ khóa 'đại biểu' trong việc tạo phương thức ẩn danh là một chút sai lệch. Bạn không tạo một đại biểu ẩn danh mà là một phương thức ẩn danh. Phương thức bạn tạo có thể được sử dụng trong một đại biểu. Như thế này:

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

8

Để hoàn thiện, điều này cũng có thể được thực hiện thông qua kết hợp phương thức Hành động / phương thức ẩn danh:

//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();
});

Invoke((Action) Process);là câu trả lời tốt nhất, cảm ơn bạn!
Jinjinov

5

Tôi gặp vấn đề với các đề xuất khác vì đôi khi tôi muốn trả về giá trị từ các phương thức của mình. Nếu bạn cố gắng sử dụng MethodInvoker với các giá trị trả về thì có vẻ như nó không thích. Vì vậy, giải pháp tôi sử dụng là như thế này (rất vui khi nghe một cách để làm cho điều này ngắn gọn hơn - Tôi đang sử dụng 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;
        }));
    }

1

Tôi thích sử dụng Action thay cho MethodInvoker, nó ngắn hơn và trông gọn gàng hơn.

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

// OR

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

Ví dụ.

// 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";
}

0

Tôi không bao giờ hiểu tại sao điều này tạo ra sự khác biệt cho trình biên dịch, nhưng điều này là đủ.

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

Phần thưởng: thêm một số xử lý lỗi, vì có thể, nếu bạn đang sử dụng Control.Invoketừ một luồng nền, bạn đang cập nhật trạng thái văn bản / tiến trình / kích hoạt của điều khiển và không quan tâm nếu điều khiển đã được xử lý.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.