Reputation: 428
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
}
}
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()
});
}
}
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
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