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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With