Я делаю библиотеку сервера, в которой пакетная ассоциация сделана перечислением.
public enum ServerOperationCode : byte
{
LoginResponse = 0x00,
SelectionResponse = 0x01,
BlahBlahResponse = 0x02
}
public enum ClientOperationCode : byte
{
LoginRequest = 0x00,
SelectionRequest = 0x01,
BlahBlahRequest = 0x02
}
Это хорошо работает, когда Вы работаете в своем собственном проекте - можно выдержать сравнение, какой перечислимый участник возвращается (т.е. if (packet.OperationCode == ClientOperationCode.LoginRequest)
). Однако, так как это - библиотека классов, пользователь должен будет определить ее собственное перечисление.
Поэтому у меня есть два перечисления для добавления как "краткий обзор" - ServerOperationCode и ClientOperationCode. Я знаю, что не возможно реализовать абстрактные перечисления в C#. Как я пошел бы, делая это?
Мне нравится использовать статические экземпляры в моих классах, когда мне это нужно. Он позволяет вам иметь некоторые значения по умолчанию, но также позволяет его расширять с помощью обычных средств наследования и реализаций интерфейса:
public abstract class OperationCode
{
public byte Code { get; private set; }
public OperationCode(byte code)
{
Code = code;
}
}
public class ServerOperationCode : OperationCode
{
public static ServerOperationCode LoginResponse = new ServerOperationCode(0x00);
public static ServerOperationCode SelectionResponse = new ServerOperationCode(0x01);
public static ServerOperationCode BlahBlahResponse = new ServerOperationCode(0x02);
public ServerOperationCode(byte code) : base(code) { }
}
public class ClientOperationCode : OperationCode
{
public static ClientOperationCode LoginRequest = new ClientOperationCode(0x00);
public static ClientOperationCode SelectionRequest = new ClientOperationCode(0x01);
public static ClientOperationCode BlahBlahRequest = new ClientOperationCode(0x02);
public ClientOperationCode(byte code) : base(code) { }
}
предполагая, что packet.OperationCode
возвращает байт, вам, вероятно, придется реализовать == оператор для байта. поместите этот код в свой абстрактный класс OperationCode.
public static bool operator ==(OperationCode a, OperationCode b)
{
return a.Code == b.Code;
}
public static bool operator !=(OperationCode a, OperationCode b)
{
return !(a == b);
}
это позволит вам выполнить ту же проверку, что и вы показали:
if (packet.OperationCode == ClientOperationCode.LoginRequest)
Некоторое время назад я написал библиотеку переключения сообщений с аналогичным сценарием, и я решил использовать дженерики для передачи определяемого пользователем перечисления. Основная проблема с этим заключается в том, что вы не можете ограничить свой общий тип только перечисляемыми типами, а можете только сказать while T: struct. Кто-то может создать экземпляр вашего типа с каким-либо другим примитивным типом (хотя использование целых чисел все еще может быть функциональным, если они все уникальные значения. Словарь выдаст исключение, если это не так. Вы можете добавить дополнительную проверку с помощью отражения в убедитесь, что вы передали перечисление.
public abstract class DefaultMessageHandler<T> : IMessageHandler<T> where T : struct {
public delegate void MessageHandlerDelegate(IMessage<T> message, IConnection connnection);
private readonly IDictionary<T, MessageHandlerDelegate> messageHandlerDictionary =
new Dictionary<T, MessageHandlerDelegate>();
protected void RegisterMessageHandler(T messageType, MessageHandlerDelegate handler) {
if (this.messageHandlerDictionary.ContainsKey(messageType))
return;
else this.messageHandlerDictionary.Add(messageType, handler);
}
protected void UnregisterMessageHandler(T messageType) {
if (this.messageHandlerDictionary.ContainsKey(messageType))
this.messageHandlerDictionary.Remove(messageType);
}
protected virtual void HandleUnregisteredMessage(IMessage<T> message, IConnection connection) {
}
void IMessageHandler<T>.HandleMessage(IMessage<T> message, IConnection connection) {
if (this.messageHandlerDictionary.ContainsKey(message.MessageType))
this.messageHandlerDictionary[message.MessageType].Invoke(message, connection);
else HandleUnregisteredMessage(message, connection);
}
}
Учитывая ваш пример сценария, вы бы просто подклассифицировали его следующим образом.
public sealed class ServerOperationHandler : DefaultMessageHandler<ServerOperationCode> {
public ServerOperationHandler() {
this.RegisterMessageHandler(ServerOperationCode.LoginResponse, this.HandleLoginResponse);
this.RegisterMessageHandler(ServerOperationCode.SelectionResponse, this.HandleSelectionResponse);
}
private void HandleLoginResponse(IMessage<ServerOperationCode> message, IConnection connection) {
//TODO
}
private void HandleSelectionResponse(IMessage<ServerOperationCode> message, IConnection connection) {
//TODO
}
}
Если вы хотите сказать, что вам нужно перечисление, которое может быть расширено клиентами библиотеки, ознакомьтесь с моей статьей CodeProject по этой теме, Символы как расширяемые перечисления .
Обратите внимание, что в моей библиотеке Symbol автоматически выбирает идентификационные номера для «значений перечисления», поскольку он предназначен для использования внутри одной программы, а не для обмена значениями в сети. Однако, возможно, можно было бы изменить Symbol.cs по своему усмотрению, чтобы клиенты могли присваивать постоянные значения символам.
Создайте Enum для LoginResponse, SelectionResponse и т. Д., Но не задают значения.
Пусть ServerOperationCode и ClientOperationCode реализуют функцию, которая, учитывая целочисленный байт-код, возвращает соответствующее значение из вашего Enum.
Пример:
public enum OperationCode
{
LoginResponse,
SelectionResponse,
BlahBlahResponse
}
public interface IOperationCodeTranslator {
public OperationCode GetOperationCode(byte inputcode);
}
public class ServerOperationCode : IOperationCodeTranslator
{
public OperationCode GetOperationCode(byte inputcode) {
switch(inputcode) {
case 0x00: return OperationCode.LoginResponse;
[...]
}
}
Предостережение: поскольку интерфейсы не могут определять статические функции, ServerOperationCode и ClientOperationCode смогут реализовать общий интерфейс, только если указанная функция является функцией экземпляра. Если им не нужно реализовывать общий интерфейс, GetOperationCode может быть статической функцией.
(Приношу извинения за неудачи с C #, это не мой родной язык ...)
Если существует база данных, совместно используемая вашим клиентским и серверным приложением, то справочные таблицы могут помочь; структура таблицы просто содержит целочисленное значение (ID) и строку (имя), эта таблица может быть заполнена любой стороной вашего приложения (клиентом или сервером) и прочитана другой стороной. Вы можете кэшировать эту таблицу (в своем коде) в словаре для быстрого поиска.
Вы также можете реализовать то же самое в файле app.config; заставьте пользователя вашей библиотеки установить эти значения в файле app.config, к которому ваша библиотека может легко получить доступ.