I have a long running task with the same name in unrelated classes. I was trying to have this code in common method using dynamic. I am getting following error
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled by user code Message=Cannot implicitly convert type 'void' to 'object'
I tried to isolate the code to the following
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 understand what is happening when I am passing in dynamic.
I haven't yet been able to track it back to something in the language, but it appears you can't have an expression lambda with dynamic expression.
Update: an expression involving a dynamic
is always of type dynamic
regardless of whether there's a void method call, see Language Aspects update
A statement lambda works:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
val.CommonLongRunningTask();
});
}
Effectively what's going on here is when the compiler encounters this:
() => val.CommonLongRunningTask()
it interprets at it as the equivalent of:
() => {return val.CommonLongRunningTask();}
...since it doesn't know at compile-time that anything you call on val
doesn't return anything. At run-time, it encounters the expression val.CommonLongRunningTask()
which is of type void
but cannot return
that value to even the lowest common denominator object
and thus throws the exception with message Cannot implicitly convert type 'void' to 'object'
You'd have the very same exception if you re-wrote your RunTask
as follows:
private static async Task RunTask(dynamic val)
{
await Task.Run(() =>
{
return val.CommonLongRunningTask();
});
}
After a bit more research/discussion it appears section 7.5.2 of the C# spec details why this is happening. Essentially anything involving a dynamic is itself a dynamic type (because at compile time the compiler cannot know how things will be bound) and thus it views "val.CommonLongRunningTask()" as something that returns dynamic (and thus has a run-time error when it realizes it's void at run-time).
Create a interface for your common method:
public interface ICommonTask
{
void CommonLongRunningTask();
}
Implement this in both classes and use it instead:
static async Task RunTask(ICommonTask val)
class ClassA : ICommonTask
class ClassB : ICommonTask
The problem is that:
() => val.CommonLongRunningTask()
translates into something:
delegate
{
return val.CommonLongRunningTask();
}
Which will fail at run time because the CommonLongRunningTask
method you're binding to doesn't have a return type.
I just had this same problem and in response to the answer from Peter as to why it happens, I came up with a solution to get Task.Run from dynamic void method to work regardless.
rewrite:
() => val.CommonLongRunningTask()
as
() => ForceDynamicExpressionBackToVoid(() => val.CommonLongRunningTask())
with the following method:
private void ForceDynamicExpressionBackToVoid(Action @dynamic)
{
@dynamic.Invoke();
}
The caveat being that you have to know that any CommonLongRunningTask you end up with at runtime is guaranteed to return void, otherwise you'll get another exception.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.