Sar*_*ath 4 .net c# dynamic async-await
我有一个长期运行的任务,在不相关的类中具有相同的名称.我试图使用动态的常用方法使用此代码.我收到了以下错误
用户代码未处理Microsoft.CSharp.RuntimeBinder.RuntimeBinderException消息=无法将类型'void'隐式转换为'object'
我试图将代码隔离到以下
class Program
{
static void Main(string[] args)
{
MainAsync();
Console.ReadKey();
}
static async void MainAsync()
{
var classA = new ClassA();
var classB = new ClassB();
await RunTask1(classA);
await RunTask1(classB);
await RunTask(classA);
await RunTask(classB);
}
static async Task RunTask(dynamic val)
{
await Task.Run(() => val.CommonLongRunningTask());
}
static async Task RunTask1(ClassA val)
{
await Task.Run(() => val.CommonLongRunningTask());
}
static async Task RunTask1(ClassB val)
{
await Task.Run(() => val.CommonLongRunningTask());
}
}
internal class ClassA
{
public void CommonLongRunningTask()
{
Console.WriteLine("Class A CommonLongRunningTask");
}
}
internal class ClassB
{
public void CommonLongRunningTask()
{
Console.WriteLine("Class B CommonLongRunningTask");
}
}
Run Code Online (Sandbox Code Playgroud)
如果我传递对象本身(RunTask1)而不是动态它可以工作.我试图了解当我在动态传递时发生了什么.
我还没有能够将它追溯到语言中的某些东西,但看起来你不能拥有带动态表达式的表达式lambda. 更新:无论是否存在void方法调用,涉及a的表达式dynamic始终为类型dynamic,请参阅语言方面更新
一个声明lambda工作:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
val.CommonLongRunningTask();
});
}
Run Code Online (Sandbox Code Playgroud)
这有效的是,当编译器遇到这个时:
() => val.CommonLongRunningTask()
Run Code Online (Sandbox Code Playgroud)
它将其解释为相当于:
() => {return val.CommonLongRunningTask();}
Run Code Online (Sandbox Code Playgroud)
...因为它在编译时不知道你调用的任何内容val都不会返回任何内容.在运行时,它遇到val.CommonLongRunningTask()类型的表达式,void但return即使是最小的公分母也不能达到该值object,因此会抛出异常消息Cannot implicitly convert type 'void' to 'object'
如果您重新编写RunTask如下内容,则会遇到同样的异常:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
return val.CommonLongRunningTask();
});
}
Run Code Online (Sandbox Code Playgroud)
在进行了一些研究/讨论之后,C#规范的第7.5.2节详细说明了为什么会发生这种情况.基本上任何涉及动态的东西本身都是动态类型(因为在编译时编译器无法知道事物是如何绑定的)因此它将"val.CommonLongRunningTask()"视为返回动态的东西(因此具有运行时错误)当它意识到它在运行时是无效的).