Как я могу сделать “абстрактное” перечисление в библиотеке классов.NET?

Я делаю библиотеку сервера, в которой пакетная ассоциация сделана перечислением.

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#. Как я пошел бы, делая это?

12
задан Lazlo 13 June 2010 в 02:59
поделиться

5 ответов

Мне нравится использовать статические экземпляры в моих классах, когда мне это нужно. Он позволяет вам иметь некоторые значения по умолчанию, но также позволяет его расширять с помощью обычных средств наследования и реализаций интерфейса:

    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)
14
ответ дан 2 December 2019 в 07:20
поделиться

Некоторое время назад я написал библиотеку переключения сообщений с аналогичным сценарием, и я решил использовать дженерики для передачи определяемого пользователем перечисления. Основная проблема с этим заключается в том, что вы не можете ограничить свой общий тип только перечисляемыми типами, а можете только сказать 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
    }
}
0
ответ дан 2 December 2019 в 07:20
поделиться

Если вы хотите сказать, что вам нужно перечисление, которое может быть расширено клиентами библиотеки, ознакомьтесь с моей статьей CodeProject по этой теме, Символы как расширяемые перечисления .

Обратите внимание, что в моей библиотеке Symbol автоматически выбирает идентификационные номера для «значений перечисления», поскольку он предназначен для использования внутри одной программы, а не для обмена значениями в сети. Однако, возможно, можно было бы изменить Symbol.cs по своему усмотрению, чтобы клиенты могли присваивать постоянные значения символам.

0
ответ дан 2 December 2019 в 07:20
поделиться
  1. Создайте Enum для LoginResponse, SelectionResponse и т. Д., Но не задают значения.

  2. Пусть 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 #, это не мой родной язык ...)

0
ответ дан 2 December 2019 в 07:20
поделиться

Если существует база данных, совместно используемая вашим клиентским и серверным приложением, то справочные таблицы могут помочь; структура таблицы просто содержит целочисленное значение (ID) и строку (имя), эта таблица может быть заполнена любой стороной вашего приложения (клиентом или сервером) и прочитана другой стороной. Вы можете кэшировать эту таблицу (в своем коде) в словаре для быстрого поиска.

Вы также можете реализовать то же самое в файле app.config; заставьте пользователя вашей библиотеки установить эти значения в файле app.config, к которому ваша библиотека может легко получить доступ.

0
ответ дан 2 December 2019 в 07:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: