0

I have various interfaces and I need to be able to call them. Here is the base class:

public class MyActorBase<TChild>: ActorBase where TChild : MyActorBase<TChild>
{
    public MyActorBase()
    {
       var actors =  ChildClass
           .GetInterfaces()
           .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IActorMessageHandler<>))
           .Select(x=> (arguments: x.GetGenericArguments(), definition: x))
           .ToImmutableList();

       if (actors.Any())
       {
           var ty = actors.First();
           var obj = Activator.CreateInstance(ty.definition, true);

           // how to call method implementation
       }

    }

    protected sealed override bool Receive(object message) => false;
    private Type ChildClass => ((this as TChild)?? new object()).GetType();
}


public interface IActorMessageHandler<in T>
{
    Task Handle(T msg);
}

I read these blog post:

  1. Dont use Activator.CreateInstance
  2. Linq Expressions
  3. Creating objects performance implications

The writers already knew the type at compile time hence were able to cast correctly. I do not know anything at compile time so I cannot use a generic method or typecast it using () operator or as operator.

UPDATE: I think people are not getting the idea of what I want to achieve. so consider this. I made a nuget package which anyone can depend upon. Somewhere in the world, someone writes this code:

    public class MyMessage
    {
        public int Number { get; }

        public MyMessage(int number) => Number = number;
    }

    public class MyNewActor: MyActorBase<MyNewActor>, IActorMessageHandler<MyMessage>
    {
        public Task Handle(MyMessage msg)
        {
            return Task.CompletedTask;
        }
    }

I want that any class that implements the IActorMessageHandler, i should be able to call its method Handle(T msg). so while I was able to instantiate it (considering that I'm not using any dependency injection) how can I call the method in the most efficient way?

Is there any alternate to reflection?

Simple Fellow
  • 4,315
  • 2
  • 31
  • 44
  • What have you tried? You should be able to use the same reflection code you have used to discover and create an instance. – Euphoric Nov 07 '19 at 13:28
  • i have read a few blog posts about it. Problem is all the blogs know the class at compile time so they typecast it correctly. My case if different. I don't know the class at compile time so I cannot typecast it by using typecasting or even as operator. – Simple Fellow Nov 07 '19 at 13:32
  • So you haven't found this https://stackoverflow.com/questions/2202381/reflection-how-to-invoke-method-with-parameters or https://dotnetcodr.com/2014/10/10/dynamically-invoking-a-method-with-reflection-in-net-c/ ? – Euphoric Nov 07 '19 at 13:32
  • "how can I call the method in the most efficient way?" - if you're doing all that reflection and LINQ *in the constructor*, frankly "efficient" already flew out the window... you might want to move `actors` to be a `static readonly` field so it only gets built once? (presumably using `typeof(TChild)` or something instead of the `this as TChild` stuff?) – Marc Gravell Nov 07 '19 at 13:33
  • Another option would be to make your message handler interface non-generic. Then it can be called directly. – Euphoric Nov 07 '19 at 13:36
  • users of my interface should be able to define their own message classes and have different implementations using the generic interface. I cannot use non generic one and want to let programmers typecast it to anything they like – Simple Fellow Nov 07 '19 at 13:39
  • Non generic version is trivial : `interface IActorMessageHandler { Type SupportedType {get;} Task Handle(object obj); }` – Euphoric Nov 07 '19 at 13:44

2 Answers2

1

What about using the dynamic keyword? This is basically optimized reflection nicely wrapped for you:

dynamic obj = Activator.CreateInstance(ty.definition, true);
Task t = obj.Handle(msg); //need to define msg before

It bypasses compile-time checks and defers method look-up at run-time.

Note that it will fail at run-time if no resolution for the Handle method can be performed.

This blog post concludes that dynamic ends up being much quicker than reflection when called fairly often because of caching optimizations.

Corentin Pane
  • 4,794
  • 1
  • 12
  • 29
1

you should not use Activator.CreateInstance it's very much expensive. instead, you may use Expression.Lamda to create objects in an efficient way.

  var object =  Expression.Lambda<Func<IActorMessageHandler<TChild>>>(Expression.New(ty.definition.Value.GetConstructor(Type.EmptyTypes) ?? throw new 
    Exception("Failed to create object"))
                        ).Compile()();
Mukul Keshari
  • 495
  • 2
  • 7
  • 1
    [This post](https://dejanstojanovic.net/aspnet/2019/february/making-reflection-in-net-work-faster/) seems to conclude that expression construction is slower than activator construction. I'm not an expert though, what do you think? – Corentin Pane Nov 07 '19 at 13:49
  • @CorentinPane I edited my post and now it contains an example of how the users should be using it, – Simple Fellow Nov 07 '19 at 13:51
  • I have done a benchmark and I can see compiled expression is the fastest one – Mukul Keshari Nov 07 '19 at 14:15