Dynamic in Task.Run
I have a long job with the same name in unrelated classes. I tried to use this code in a generic method using dynamic. I am getting the following error
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was not handled by user code Message = Cannot implicitly convert 'void' to 'object'
I tried to highlight the code as follows
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");
}
}
If I pass the object itself (RunTask1) instead of dynamic it works. I am trying to figure out what happens when I pass dynamically.
source to share
I haven't been able to track it down in some language yet, but it looks like you can't have a lambda expression with dynamic expression. Update: an expression involving a is dynamic
always of type dynamic
, regardless of whether a void method call exists, see Updating Language Aspects
Lambda operation:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
val.CommonLongRunningTask();
});
}
Update:
Effectively what happens here when the compiler encounters this:
() => val.CommonLongRunningTask()
it interprets on it as equivalent:
() => {return val.CommonLongRunningTask();}
... since it is not known at compile time that whatever you call val
returns nothing. At run time, it encounters an expression val.CommonLongRunningTask()
that has a type void
, but cannot return
that value even for the lowest common denominator, object
and thus throws an exception with the messageCannot implicitly convert type 'void' to 'object'
You will have the same exception if you republish yours RunTask
like this:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
return val.CommonLongRunningTask();
});
}
Language aspects
After a bit more research / discussion in section 7.5.2 describing the C # spec, why this happens. Basically anything related to dynamics is itself a dynamic type (since at compile time the compiler cannot know how things will be related) and therefore it treats "val.CommonLongRunningTask ()" as something that returns a dynamic ( and therefore has a runtime error when it implements it unchanged at runtime).
source to share
I just had the same problem, and in response to Peter's answer on why this is happening, I came up with a solution to get Task.Run from a dynamic void method to work independently.
rewritten:
() => val.CommonLongRunningTask()
and
() => ForceDynamicExpressionBackToVoid(() => val.CommonLongRunningTask())
with the following method:
private void ForceDynamicExpressionBackToVoid(Action @dynamic)
{
@dynamic.Invoke();
}
The caveat is that you should be aware that any instance of CommonLongRunningTask that you end up with at runtime is guaranteed to return void, otherwise you will get another exception.
source to share