我在一个不相关的类中有一个同名的长任务。我试图使用动态的常用方法使用此代码。 我收到了以下错误
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException未被用户代码处理 Message =无法将类型'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");
}
}
如果我传递对象本身(RunTask1)而不是动态它可以工作。我试图了解当我在动态传递时发生了什么。
答案 0 :(得分:6)
我还没能用语言跟踪它,但看起来你不能拥有带动态表达的lambda表达式。 更新:涉及dynamic
的表达式始终为dynamic
类型,无论是否有无效方法调用,请参阅语言方面更新
语句lambda起作用:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
val.CommonLongRunningTask();
});
}
有效地,这里发生的是编译器遇到这个时:
() => val.CommonLongRunningTask()
它将其解释为相当于:
() => {return val.CommonLongRunningTask();}
...因为它在编译时并不知道你在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();
});
}
经过一番研究/讨论之后,C#规范第7.5.2节详细说明了为什么会这样。基本上任何涉及动态的东西本身都是动态类型(因为在编译时编译器无法知道事物是如何绑定的)因此它会查看" val.CommonLongRunningTask()"作为返回动态的东西(因此当它意识到它在运行时是无效的时候会出现运行时错误)。
答案 1 :(得分:1)
为您的常用方法创建一个界面:
public interface ICommonTask
{
void CommonLongRunningTask();
}
在两个类中实现它并改为使用它:
static async Task RunTask(ICommonTask val)
class ClassA : ICommonTask
class ClassB : ICommonTask
答案 2 :(得分:1)
问题在于:
() => val.CommonLongRunningTask()
转化为某种东西:
delegate
{
return val.CommonLongRunningTask();
}
哪个会在运行时失败,因为您绑定的CommonLongRunningTask
方法没有返回类型。
答案 3 :(得分:0)
我只是遇到了同样的问题,并且为了回应彼得的回答 重写: 作为 使用以下方法: 需要注意的是,您必须知道在运行时最终得到的任何CommonLongRunningTask都会保证返回void,否则您将获得另一个异常。() => val.CommonLongRunningTask()
() => ForceDynamicExpressionBackToVoid(() => val.CommonLongRunningTask())
private void ForceDynamicExpressionBackToVoid(Action @dynamic)
{
@dynamic.Invoke();
}