Alde
Alde

Reputation: 428

C# reflection get all subclasses of certain class and call generic method with their type

I have packets :

public class HelloPacket
{
    public string MyName { get; set; }
}

public class PingPacket
{
    public float PingTime { get; set; }
}

I have a generic abstract 'handler' class like this :

public abstract class PacketHandler<T>
{
    public abstract void Handle(T t);
}

And their implementations :

public class HelloPacketHandler : PacketHandler<HelloPacket>
{
    public override void Handle(HelloPacket helloPacket)
    {
        Console.WriteLine("Received Hello packet!");
    }
}

public class PingPacketHandler : PacketHandler<PingPacket>
{
    public override void Handle(PingPacket pingPacket)
    {
        Console.WriteLine("Pong!");
    }
}

I need to register them like this :

        NetPacketProcessor _netPacketProcessor = new NetPacketProcessor();

        HelloPacketHandler helloHandler = new HelloPacketHandler();
        PingPacketHandler pingHandler = new PingPacketHandler();

        _netPacketProcessor.SubscribeReusable<HelloPacket>((packet) => { helloHandler.Handle(packet); });
        _netPacketProcessor.SubscribeReusable<PingPacket>((packet) => { pingHandler.Handle(packet); });

Here is the SubscribeReusable method :

public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new()
{
}

The problem is that, as the solution grows, I will have to keep adding these calls.

I'm wondering if I could use reflection to dynamically call 'SubscribeReusable'. The problem I'm facing is with the generics.

Here is the code I started with :

        foreach (Type i in typeof(PacketHandler).Assembly.GetTypes())
        {
            if (typeof(PacketHandler).IsAssignableFrom(i) && !i.IsAbstract && !i.IsInterface)
            {
                // Subscribe here
            }
        }

Where I'm stuck

I tried Type and dynamic, but it is still not working. I'm wondering if I even have the right approach.

 foreach (dynamic i in typeof(PacketHandler<dynamic>).Assembly.GetTypes())
            {
                if (typeof(PacketHandler<dynamic>).IsAssignableFrom(i) &&
                    !i.IsAbstract && !i.IsInterface)
                {
                    PacketHandler<dynamic> pkt = (PacketHandler<dynamic>) Activator.CreateInstance(i);

                    Type t = pkt.GetType();

                    MethodInfo method = typeof(NetPacketProcessor).GetMethod(nameof(NetPacketProcessor.SubscribeReusable));
                    MethodInfo generic = method.MakeGenericMethod(t);
                    generic.Invoke(this, new object[]
                    {
                        pkt.Handle()
                    });
                }
            }

Error

Edit : I have updated the code according to the answer given by Alexander :

    private void RegisterPacketHandlers()
    {
        Console.WriteLine("Using reflection to subscribe handlers...");
        
        var baseType = typeof(PacketHandler<>);
        var assembly = baseType.Assembly;
        foreach (var derivedType in assembly.GetTypes()
            .Where(t => !t.IsAbstract &&
                        !t.IsInterface &&
                        t.BaseType != null &&
                        t.BaseType.IsGenericType &&
                        t.BaseType.GetGenericTypeDefinition() == baseType))
        {
            
            Console.WriteLine("Found class with type : " + derivedType.FullName);

            //

            try
            {
                
                // TODO Generate a delegate and hook into your processor

                var mi = derivedType.GetMethod("Handle");
                var action = DelegateBuilder.BuildDelegate<Action<object, int>>(mi);

                _netPacketProcessor.SubscribeReusable(action);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error : " + e);
            }
        }
    }

I'm still trying to call SubscribeReusable using the Derived type

Upvotes: 0

Views: 618

Answers (1)

Alexander Schmidt
Alexander Schmidt

Reputation: 5723

For the first part of your question you could use the following snippet to get all direct children of PacketHandler<T>:

var baseType = typeof(PacketHandler<>);
var assembly = baseType.Assembly;
foreach (var derivedType in assembly.GetTypes()
    .Where(t => !t.IsAbstract && 
                !t.IsInterface && 
                t.BaseType != null && 
                t.BaseType.IsGenericType && 
                t.BaseType.GetGenericTypeDefinition() == baseType))
{
    // TODO Generate a delegate and hook into your processor
}

The second part (calling delegate) can be solved using the approaches here.

Upvotes: 3

Related Questions