2010-06-13 1 views
11

Я делаю серверную библиотеку, в которой ассоциация пакетов выполняется перечислением.Как я могу сделать «абстрактное» перечисление в библиотеке классов .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#. Как мне это сделать?

+2

Я не могу понять, что вы пытаетесь здесь. Вы хотите, чтобы ваши коды были реальными .NET-типами, поэтому ваш клиент получает проверку типов, но не хочет, чтобы клиент видел эти значения? Что вы пытаетесь сделать, что клиент, добавляющий ссылку на вашу библиотеку, не решает? –

+1

Я думаю, что на самом деле я пытаюсь создать библиотеку, в которой статические объекты могут быть расширены, что невозможно. Например, библиотека, в которой вы можете переопределить или реализовать «Server.Start», но это не совсем возможно. Я мог бы пойти по событиям. Я посмотрю, что я могу сделать, спасибо за все ответы, я попробую их. – Lazlo

ответ

13

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

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)
0

Если вы хотите сказать, что хотите перечислить, которые могут быть расширены клиентами библиотеки, ознакомьтесь с моей статьей CodeProject на эту тему, Symbols as extensible enums.

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

0
  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

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

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

0

Я написал сообщение переключающий библиотеку с подобным сценарием некоторое время назад, и я решил использовать дженерики передать определяемое пользователем перечисление. Основная проблема заключается в том, что вы не можете ограничить свой общий тип только перечисляемыми типами, но можете только сказать, когда T: struct. Кто-то может создать экземпляр вашего типа с помощью некоторого другого примитивного типа (хотя использование ints все еще может быть функциональным, если все они являются уникальными значениями. Словарь генерирует исключение, если это не так. Вы могли бы добавить дополнительную проверку, используя отражение убедитесь, что вы пройти перечисление.

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 
    } 
} 
4

Почему все думают, что Перечисления не может абстрагироваться?

Класс System.Enum IS абстракция перечисления.

Вы можете назначить любое значение перечисления для Enum, и вы можете вернуть его обратно в исходное перечисление или использовать имя или значение.

например:

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

/// <summary> 
    /// creates a new trigger property. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="value"></param> 
    /// <param name="name"></param> 
    /// <returns></returns> 
    protected virtual TriggerProperty<T> Create<T>(T value, Enum name) 
    { 
     var pt = new TriggerProperty<T>(value, OnPropertyChanged, Enum.GetName(name.GetType(), name)); 
     _properties[name.GetHashCode()] = pt; 
     return pt; 
    } 

Я использую Enum.GetName(Type, object), чтобы получить имя значения перечисления (указать имя для свойства) , а также из соображений скорости и последовательности я использую GetHashCode() возвращать целое значение элемента перечисления (хэш-код для Int всегда просто INT значение)

Это пример метода называют:

public enum Props 
    { 
     A, B, C, Color, Type, Region, Centre, Angle 
    } 

    public SpecularProperties() 
     :base("SpecularProperties", null) 
    { 
     Create<double>(1, Props.A); 
     Create<double>(1, Props.B); 
     Create<double>(1, Props.C); 
     Create<Color>(Color.Gray, Props.Color); 
     Create<GradientType>(GradientType.Linear, Props.Type); 
     Create<RectangleF>(RectangleF.Empty, Props.Region); 
     Create<PointF>(PointF.Empty, Props.Centre); 
     Create<float>(0f, Props.Angle); 
    } 
+0

Это ТОЧНО ответ, который мне нужен здесь. Жаль, что я не смог бы это сделать. Это понимание позволило мне создать словарь в абстрактном базовом классе, который будет создан несколькими подклассами, каждый из которых имеет собственное определение перечисления. Отлично! – kmote

0

Как использовать статический словарь и виртуальный метод для извлечения статических словарей в унаследованных классах?

Как последуйте для Вашего случая:

public abstract class Operation 
    { 
     protected abstract Dictionary<string, int> getCodeTable(); 
     public int returnOpCode(string request){ return getCodeTable()[request]; } 
    } 
    public class ServerOperation : Operation 
    { 
     Dictionary<string, int> serverOpCodeTable = new Dictionary<string, int>() 
     { 
      {"LoginResponse", 0x00,}, 
      {"SelectionResponse", 0x01}, 
      {"BlahBlahResponse", 0x02} 
     }; 
     protected override Dictionary<string, int> getCodeTable() 
     { 
      return serverOpCodeTable; 
     } 

    } 
    public class ClientOperation : Operation 
    { 
     Dictionary<string, int> cilentOpCodeTable = new Dictionary<string, int>() 
     { 
      {"LoginResponse", 0x00,}, 
      {"SelectionResponse", 0x01}, 
      {"BlahBlahResponse", 0x02} 
     }; 
     protected override Dictionary<string, int> getCodeTable() 
     { 
      return cilentOpCodeTable; 
     } 
    } 

 Смежные вопросы

  • Нет связанных вопросов^_^