有没有办法导致传递给Control.BeginInvoke的委托的类型推断?

Control.BeginInvoke

在此处输入图像描述

在此处输入图像描述

在这两种情况下,似乎很清楚编译器具有推断委托类型所需的所有信息。 然而,在这两种情况下,类型推断似乎都不起作用:

  BeginInvoke(myMethodThatTakesNoParams); 

产生编译器错误

错误105’System.Windows.Forms.Control.BeginInvoke(System.Delegate)’的最佳重载方法匹配有一些无效的参数

同样如此

 BeginInvoke(ShowProcessErrors, new object[] { process }); 

两个方法调用只有在我将其更改为explitly创建委托并传递它时才会编译。 以下两种编译都很好:

 BeginInvoke(new MethodInvoker(myMethodThatTakesNoParams)); 

 BeginInvoke(new ProcessErrorDelegate(ShowProcessErrors), new object[] { process }); 

似乎没有任何明显的理由为什么类型推断在这里不起作用。 有没有办法调用BeginInvoke而不显式创建委托?

问题是myMethodThatTakesNoParams不是真正的委托,而是编译器所谓的“方法组”。 方法组不是CLR中的真实类型。 必须将其转换为要使用的委托类型。 当您使用这样的方法组时:

 Action a = myMethodThatTakesNoParams; 

编译器识别出您要将方法组转换为委托并为您插入转换。 它产生的IL有效地说:

 Action a = new Action(myMethodThatTakesNoParams); 

当你说:

 Delegate d = myMethodThatTakesNoParams 

编译器实际上不知道该怎么做。 从理论上讲,它可以为您选择任何兼容的委托类型,但C#通常不会将您未使用的类型插入到表达式中。 由于它不知道您希望方法组转换为什么委托,因此编译器会产生错误。

我在我的示例中使用了变量赋值,但是相同的逻辑适用于方法的参数。

解决方法是编写自己的扩展方法,其中包含特定的委托类型:

 static class ControlExtensions { public static IAsyncResult BeginInvoke(this Control c, Action a) { return c.BeginInvoke(a); } } 

这通常是.NET程序员的一个惊喜,第15.1节中的C#语言特定说明:

请注意,System.Delegate本身不是委托类型; 它是一个类类型,从中派生所有委托类型

当然,没有方法转换为类。 BeginInvoke()的第一个参数必须是委托类型,以保持编译器满意。 也许这听起来像是一个任意的限制,绝对不是。 代表们非常重要的一点是他们是类型安全的 。 在像C#这样的静态类型语言中,这是一个非常重要的事情。 您不能使用太少的参数或太多的参数或错误类型的参数来调用委托。 在创建委托时检查,当您仍然穿着睡衣或舒适的隔间时,会出现编译时错误。 在运行时没有任何意外,您的程序突然在当天最不合时宜的时间内发挥作用。 此类型检查当然不适用于Delegate。 所以它不是委托类型。

确实与Control.BeginInvoke()有关,它使用后门来获取调用的方法。 Math.Pi当您传递Math.Pi而不是进度时 ,在运行代码之前无法找到。 这不是一个令人愉快的例外,因为不清楚是否你的BeginInvoke()调用错误或者被调用的方法是否引发exception。 实际上Invoke()的问题更多。

Anyhoo,必须给编译器一个委托,不止一种方法:

古老的匿名方法语法在这种情况下仍然可以很好地工作:

  this.BeginInvoke(delegate() { ShowProcessErrors(process); }); 

你已经找到了MethodInvoker,我通常会选择Action,因为它更短:

  this.BeginInvoke(new Action(() => ShowProcessErrors(process))); 

当然,你总是可以通过扩展方法让编译器满意:

  this.BeginInvoke(() => ShowProcessErrors(process)); 

有:

 static class Extensions { public static void BeginInvoke(this Control ctl, Action a) { ctl.BeginInvoke(a); } }