Invoke 调用中的匿名方法

发布于 2024-07-07 20:28:50 字数 355 浏览 10 评论 0原文

在我们想要在 Control.Invoke 中匿名调用委托的语法上遇到了一些问题。

我们尝试了多种不同的方法,但都没有效果。

例如:

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

where someParameter is local to this method

上面将导致编译器错误:

无法将匿名方法转换为“System.Delegate”类型,因为它不是委托类型

Having a bit of trouble with the syntax where we want to call a delegate anonymously within a Control.Invoke.

We have tried a number of different approaches, all to no avail.

For example:

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

where someParameter is local to this method

The above will result in a compiler error:

Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

在梵高的星空下 2024-07-14 20:28:51

因为 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;});

警告:如果使用捕获异步,则需要小心一点,但是同步 很好 - 即上面的很好)

另一个选择是编写一个扩展方法:

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 基类中。

Because Invoke/BeginInvoke accepts Delegate (rather than a typed delegate), you need to tell the compiler what type of delegate to create ; MethodInvoker (2.0) or Action (3.5) are common choices (note they have the same signature); like so:

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

If you need to pass in parameters, then "captured variables" are the way:

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

(caveat: you need to be a bit cautious if using captures async, but sync is fine - i.e. the above is fine)

Another option is to write an extension method:

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

then:

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

You can of course do the same with BeginInvoke:

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

If you can't use C# 3.0, you could do the same with a regular instance method, presumably in a Form base-class.

嘿嘿嘿 2024-07-14 20:28:51

实际上你不需要使用 delegate 关键字。 只需传递 lambda 作为参数:

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

Actually you do not need to use delegate keyword. Just pass lambda as parameter:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
倾城月光淡如水﹏ 2024-07-14 20:28:51
myControl.Invoke(new MethodInvoker(delegate() {...}))
myControl.Invoke(new MethodInvoker(delegate() {...}))
余生一个溪 2024-07-14 20:28:51

您需要创建一个委托类型。 匿名方法创建中的关键字“委托”有点误导。 您不是创建匿名委托,而是创建匿名方法。 您创建的方法可以在委托中使用。 像这样:

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

You need to create a delegate type. The keyword 'delegate' in the anonymous method creation is a bit misleading. You are not creating an anonymous delegate but an anonymous method. The method you created can be used in a delegate. Like this:

myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
染年凉城似染瑾 2024-07-14 20:28:51

为了完整起见,这也可以通过 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();
});

For the sake of completeness, this can also be accomplished via an Action method/anonymous method combination:

//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();
});
╰沐子 2024-07-14 20:28:51

我对其他建议有疑问,因为我有时想从我的方法中返回值。 如果您尝试将 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;
        }));
    }

I had problems with the other suggestions because I want to sometimes return values from my methods. If you try to use MethodInvoker with return values it doesn't seem to like it. So the solution I use is like this (very happy to hear a way to make this more succinct - I'm using 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;
        }));
    }
寻找一个思念的角度 2024-07-14 20:28:51

我喜欢使用 Action 代替 MethodInvoker,它更短并且看起来更干净。

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

// OR

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

例如。

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

I like to use Action in place of MethodInvoker, it is shorter and looks cleaner.

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

// OR

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

Eg.

// 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";
}
我不在是我 2024-07-14 20:28:51

我一直不明白为什么这会对编译器产生影响,但这已经足够了。

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) { }
    }
}

I never understood why this makes a difference for the compiler, but this is sufficient.

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

Bonus: add some error handling, because it is likely that, if you are using Control.Invoke from a background thread you are updating the text / progress / enabled state of a control and don't care if the control is already disposed.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
夜灵血窟げ 2024-07-14 20:28:51

我需要它来显示来自单独线程的模式对话框,但实际上我也可以从中获取返回值。 Vokinneberg 的答案让我走上了正轨,但我仍然需要更多东西。

我想出的最终解决方案是将其添加为 MethodInvoker 的函数版本:

public delegate Object FunctionInvoker();

现在,如果我有一个像这样的函数,它显示一个模式对话框并从中返回数据

Dictionary<int, string> ShowDataDialog(byte[] data, Form parent)
{
    using (DataDialog dd = new DataDialog(data))
    {
        if (dd.ShowDialog(parent) != DialogResult.OK)
            return null;
        return dd.MappedData;
    }
}

...。 ..可以从不同的线程中这样调用:

Dictionary<int, string> mappedData =
    (Dictionary<int, string>)parent.Invoke(
         (FunctionInvoker)(() => ShowNewFromImageDialog(data, parent)));

现在,一切正常:在主窗体上调用该函数,对话框显示为主窗体的模式对话框,并且对话框中的值返回到另一个线程,它可以继续处理。

那里仍然有一些丑陋的强制转换,因为 Control.Invoke 碰巧返回一个对象,但它仍然可以用于任何函数,只要不涉及输出参数等高级内容。

如果我使用泛型,可能可以使这个更清晰,作为 Form 上的扩展方法或其他东西,在内部进行强制转换。 但现在,这样就可以了。

I needed this to show a modal dialog from a separate thread, but in a way that I could actually get a return value from it, too. Vokinneberg's answer put me on the right track, but I still needed something more.

The final solution I came up with was to add this, as function-version of MethodInvoker:

public delegate Object FunctionInvoker();

Now, if I have a function like this, which shows a modal dialog and returns data from it...

Dictionary<int, string> ShowDataDialog(byte[] data, Form parent)
{
    using (DataDialog dd = new DataDialog(data))
    {
        if (dd.ShowDialog(parent) != DialogResult.OK)
            return null;
        return dd.MappedData;
    }
}

...it can be called like this from the different thread:

Dictionary<int, string> mappedData =
    (Dictionary<int, string>)parent.Invoke(
         (FunctionInvoker)(() => ShowNewFromImageDialog(data, parent)));

Now, everything works: the function is invoked on the main form, the dialog is shown as modal dialog of the main form, and the value from the dialog is returned to the other thread, which can continue its processing.

There's still some ugly casts in there, because Control.Invoke happens to return an Object, but still, it can be used for any function, as long as there are no advanced things like output-parameters involved.

It's probably possible to make this cleaner, as extension method on Form or something, that does the casts internally, if I work with generics. But for now, this'll do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文