.net Invoke调用中的匿名方法

kxxlusnw  于 2023-02-10  发布在  .NET
关注(0)|答案(9)|浏览(183)

在Control.Invoke中匿名调用委托的语法上有一点问题。
我们尝试了许多不同的方法,但都无济于事。
例如:

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

其中someParameter是此方法的本地参数
上述操作将导致编译器错误:
无法将匿名方法转换为类型"System.Delegate",因为它不是委托类型

afdcj2ne

afdcj2ne1#

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

gg58donl

gg58donl2#

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

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

wvmv3b1j3#

myControl.Invoke(new MethodInvoker(delegate() {...}))
rlcwz9us

rlcwz9us4#

你需要创建一个委托类型。在匿名方法创建中的关键字“delegate”有点误导。你不是在创建匿名委托,而是在创建匿名方法。你创建的方法可以在委托中使用。如下所示:

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

jutyujz05#

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

3df52oht6#

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

wbgh16ku7#

我喜欢用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";
}
s1ag04yj

s1ag04yj8#

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

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

额外的好处:添加一些错误处理,因为如果您从后台线程使用Control.Invoke,您很可能正在更新控件的text / progress / enabled状态,而不关心控件是否已经被释放。

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

l2osamch9#

我需要它来显示来自一个单独线程的模态对话框,但在某种程度上,我实际上也可以从它获得一个返回值。Vokinneberg's answer使我走上了正确的道路,但我仍然需要更多的东西。
我想到的最后一个解决方案是添加以下内容,作为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碰巧返回了一个Object,但是它仍然可以用于任何函数,只要不涉及像输出参数这样的高级东西。
如果我使用泛型的话,也许可以把它做得更干净,比如Form上的扩展方法或者其他什么,在内部进行强制转换,但是现在,这样就可以了。

相关问题