将方法组隐式转换为委托(用于Control.Invoke的参数)

11

我正在开发一个Windows Forms应用程序,其中包含自定义控件,这些控件的方法可能会从UI线程以外的线程调用。因此,为了防止异常,这些方法看起来像这样:

public void DoSomeStuff()
{
    if (InvokeRequired)
    {
        Invoke((Action)DoSomeStuff);
    }
    else
    {
        // Actually do some stuff.
    }
}

将方法组DoSomeStuff显式转换为Action引起了我的注意,因此我比以前深入研究了委托和其他相关主题。

虽然我在这里看到了一些相关的问题,但我没有找到确切的答案,即:

为什么在这种情况下方法组DoSomeStuff需要显式转换为Action

如果我去掉转换,那么就会出现两个错误:

  

错误102 参数1:无法将'method group'转换为   'System.Delegate'

     

错误101 'System.Windows.Forms.Control.Invoke(System.Delegate,   object[])'的最佳重载方法有一些无效的参数

编译器似乎不能确定使用哪种Invoke重载的事实,这似乎是一个相当大的提示,但我仍然不确定它为什么无法弄清楚。我希望编译器能够推断应该使用第一个Invoke重载,它接受单个Delegate参数。

我之所以这样希望,是因为如果代码像这样编写,就不会出现问题:

Action a = DoSomeStuff;
Invoke(a);

方法组DoSomeStuff可以隐式转换为Action委托类型,而ActionSystem.Delegate派生(在技术上?),因此Invoke可以毫不费力地处理参数a。但是,当我尝试直接将DoSomeStuff作为参数传递时,为什么编译器不能进行隐式转换呢?老实说,我对自己的逻辑并不完全信服,但我仍然不确定自己错了哪里。

2个回答

3

使用C# 10和Visual Studio 2022,您不再需要显式转换(自然函数类型)。


3
问题不在于编译器难以选择重载。 "最佳匹配" 重载是您想要的,但它具有无效参数。 C# 语言没有定义从方法组(DoSomeStuff)到 System.Delegate 的任何隐式转换。
您可能会说编译器应该只选择一个 Action/Func 类型,这已被请求作为一种语言特性。 目前,这不是 C# 的一部分。(我不知道为什么;希望语言请求能够通过。) System.Windows.Forms.Control.Invoke 是在 .NET 1.0 中创建的。今天,人们会使用以下签名:
void Invoke(Action action);
Task InvokeAsync(Action action);

这样做将会简单易行。

尝试使用await来进行迁移,这个问题便不再是一个关注点。


你可能会说编译器应该只选择Action/Func类型之一,并且这已被请求为一种语言特性。但是,这很容易崩溃:void Foo(out string abc);无法使用ActionFunc表示。如果他们将Action/Func推断实现为一种语言特性,那么该特性将被破坏。 - GeirGrusom
1
@GeirGrusom,使用这种特殊签名可能会导致转换失败(静态地)。这是一个罕见的情况,我认为在这里显示错误是可以的。并非所有方法都可以表示为Action / Func,但在实践中有99%可以。所有委托类型应该与结构上进行处理,在适当的情况下进行隐式转换。 - usr
我认为这不太好。该错误是因为该特性基本上已经损坏了。 - GeirGrusom
2
我不太明白你在这里说什么,GeirGrusom。为什么编译器不能推断出某些特殊情况下方法是Action还是Func,就意味着在所有情况下都应该显示错误?按照这个逻辑,原始问题中将DoSomeStuff隐式转换为Action也应该是一个错误吧? - Adam Goodwin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接