Reputation: 1604
I'm trying to invoke a method on a generic type (generic is also an argument to invoking method) and are hitting RuntimeBinderException
that I don't really see how to work around.
As you can see in below example the instance returns the MyHandler
instance (as a dynamic
) which I expect to get invoked, but without explicit conversion to the generic type of the DTO (as MyDto
) it will crash. And my requirement is to determine the type in runtime (as I see the JsonConvert.DeserializeObject(data, type)
does, until the point of invocation).
static async Task Main(string[] args)
{
string dataType = typeof(MyDto).AssemblyQualifiedName;
string data = JsonConvert.SerializeObject(new MyDto());
var type = Type.GetType(dataType);
var dto = JsonConvert.DeserializeObject(data, type);
var handler = GetHandler();
await handler.HandleMessageAsync(dto, CancellationToken.None); // throws the RuntimeBinderException
await handler.HandleMessageAsync(dto as MyDto, CancellationToken.None); // is able to invoke MyHandler.HandleMessageAsync
}
public static dynamic GetHandler()
{
// this is simplified logic for locating a handler instance.
return new MyHandler();
}
The exception thrown in the 1st invocation from example is
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'The best overloaded method match for 'ConsoleApp.MessageHandlerBase<ConsoleApp.MyDto>.HandleMessageAsync(ConsoleApp.MyDto, System.Threading.CancellationToken)' has some invalid arguments'
I don't see how the conversion can make a change to the DTO as they are the same type. So, my question is how I can succeed invoking the method completely dynamically?
This is the simplified DTO and handler service class hierarchy of the real implementation:
public class MyDto
{
public int Prop1 { get; set; } = 1;
}
public interface IMessageHandler<in T> where T : class
{
Task HandleMessageAsync(T dto, CancellationToken cancellationToken);
}
public class MyHandler : IMessageHandler<MyDto>
{
public Task HandleMessageAsync(MyDto dto, CancellationToken cancellationToken)
{
Console.WriteLine("OnError called from " + this);
return Task.CompletedTask;
}
}
Upvotes: 1
Views: 200
Reputation: 2540
Can you please try changing type of dto variable from object to dynamic as below -
dynamic dto = JsonConvert.DeserializeObject(data, type);
The final code should something like this -
static async Task Main(string[] args)
{
string dataType = typeof(MyDto).AssemblyQualifiedName;
string data = JsonConvert.SerializeObject(new MyDto());
var type = Type.GetType(dataType);
dynamic dto = JsonConvert.DeserializeObject(data, type); // declare as dynamic instead of var (declaring as var would default to object type)
var handler = GetHandler();
await handler.HandleMessageAsync(dto, CancellationToken.None); // throws the RuntimeBinderException
await handler.HandleMessageAsync(dto as MyDto, CancellationToken.None); // is able to invoke MyHandler.HandleMessageAsync
}
Upvotes: 1