C# “如何检测”;“消息类型”-&引用;消息处理程序";使用反射的关系
我有以下情况。 我有一个C#控制台应用程序,它接收字节[]格式的TCP数据 我有一个名为OnNetworkReceive的方法,它接收字节[]C# “如何检测”;“消息类型”-&引用;消息处理程序";使用反射的关系,c#,C#,我有以下情况。 我有一个C#控制台应用程序,它接收字节[]格式的TCP数据 我有一个名为OnNetworkReceive的方法,它接收字节[] public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { var packetType = (PacketType)reader.GetByte();
public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod)
{
var packetType = (PacketType)reader.GetByte();
var packet = NetworkUtils.ResolvePacket(packetType, reader);
HandlerRegistry.Handlers[packetType].Handle(packet);
reader.Recycle();
}
var packetType=(packetType)reader.GetByte()
从字节数组中读取第一个字节并获取消息的类型var packet=NetworkUtils.resolvepack(packetType,reader)代码>将消息的其余部分解析为C#struct
HandlerRegistry.Handlers[packetType].Handle(数据包)代码>通过使用具有以下结构的预定义字典调用适当的处理程序
public class StartBattleRequestHandler : IPacketHandler
{
public void Handle(INetPacket packet)
{
var request = (StartBattleRequest)packet;
System.Console.WriteLine($"[StartBattle] Attacker ({request.AttackerArmyId}) vs Defender ({request.DefenderArmyId})");
}
}
我的问题是: 您能建议我如何在启动时使用反射自动填充HandlerRegistry.Handlers字典吗 我对反射有很好的理解,例如,我知道我可以搜索实现IPacketHandler的所有类,但问题是我如何知道与此消息处理程序关联的PacketType是什么 我的一个想法是,每个PacketHandler都有一个readonly常量属性,它告诉我们与这个him关联的PacketType是什么
有什么想法吗?您有几种选择。我在下面按个人喜好列出其中三个。我相信还有其他方法可以做到这一点,但我想到的是: 选项1-模块初始值设定项注册 一种选择是使用C#9的模块初始化器和自注册功能。模块初始值设定项保证在程序集中任何其他代码之前运行。您可以声明多个对此功能有用的选项。首先,我们可以将您的注册表更改为以下内容:
公共静态类HandlerRegistry
{
内部静态无效寄存器(PacketType类型,IPacketHandler处理程序)
=>处理程序[类型]=处理程序;
内部静态无效寄存器(PacketType类型),其中THandler:ipackthandler,new()
=>处理程序[type]=new THandler();
私有静态只读字典处理程序=new();
公共静态bool TryGetHandler(PacketType类型,out-iPackHandler处理程序)
=>Handlers.TryGetValue(类型,out handler);
}
然后让每个处理程序类型自己注册。我添加了两种不同的Register
方法,具体取决于您希望如何使用它们。从每个类中,我们需要调用其中一个方法。模块初始化属性
必须放在内部
或公共
静态
方法上,我们可以选择使用编辑器或浏览器属性
对intellisense隐藏它:
公共类StartBatterRequestHandler:iPackHandler
{
[模块初始化器]
[EditorBrowsable(EditorBrowsableState.Never)]
内部静态void Initialize()
=>HandlerRegistry.Register(PacketType.StartBattlerRequest,新的StartBattlerRequestHandler());
公共无效句柄(inetpack)
{
var请求=(StartBattlerRequest)数据包;
System.Console.WriteLine($“[StartBattle]攻击者({request.AttackerArmyId})vs防御者({request.DefenderArmyId})”);
}
}
公共类EndTurnRequestHandler:IPacketHandler
{
[模块初始化器]
[EditorBrowsable(EditorBrowsableState.Never)]
内部静态void Initialize()
=>HandlerRegistry.Register(PacketType.EndTurnRequest);
公共无效句柄(inetpack)
{
var请求=(EndTurnRequest)数据包;
System.Console.WriteLine($“[EndTurn]TurnId{request.TurnId}”);
}
}
然后我们可以使用如下处理程序:
static void Main(字符串[]args)
{
var startRequest=new StartBattleRequest();
var endRequest=new EndTurnRequest();
if(HandlerRegistry.TryGetHandler(startRequest.Type,out var handler1))
手柄1.手柄(startRequest);
if(HandlerRegistry.TryGetHandler(endRequest.Type,out var handler2))
handler2.句柄(endRequest);
}
哪些产出:
[StartBattle]攻击者(0)对防御者(0)
[EndTurn]转盘编号0
注意,我假设这里可以使用INetPacket.PacketType
;否则您将直接传递PacketType.startBatterRequest
。我还在EndTurnRequest
上创建了一个属性,仅用于演示目的
现在,上述方法的缺点是,您需要使用额外的静态方法使处理程序变得混乱;但它确实提供了一个很好的机制,即使是从外部库(假设它们遵循约定并且是静态引用的)也能保证注册
选项2-反射和属性
第二种方法是使用反射和我们将应用于处理程序的新属性来查询类型。这允许您通过使用属性选择加入(或退出),而不仅仅是接受实现接口的任何类型
属性:
[AttributeUsage(AttributeTargets.Class)]
公共密封类HandlerRegisterAttribute:属性
{
公共HandlerRegisterAttribute(PacketType)=>PacketType=type;
公共PacketType PacketType{get;}
}
然后将此属性添加到类中:
[HandlerRegister(PacketType.startBattlerRequest)]
公共类StartBatterRequestHandler:IPacketHandler
{
}
[HandlerRegister(PacketType.EndTurnRequest)]
公共类EndTurnRequestHandler:IPacketHandler
{
}
现在是棘手的部分。我们需要建立以下类别的列表:
- 实现我们的接口
- 用
HandlerRegisterAttribute
- 有一个无参数构造函数
- 这些都不是抽象的
HandlerRegistry
现在如下所示:
公共静态类HandlerRegistry
{
私人街
public enum PacketType : byte
{
None = 0,
ConfirmLoadingBattleScene = 1,
EndTurnRequest = 2,
}
public class StartBattleRequestHandler : IPacketHandler
{
public void Handle(INetPacket packet)
{
var request = (StartBattleRequest)packet;
System.Console.WriteLine($"[StartBattle] Attacker ({request.AttackerArmyId}) vs Defender ({request.DefenderArmyId})");
}
}