2010-06-13 12 views
11

Estoy creando una biblioteca de servidor en la que la asociación de paquetes se realiza por enum.¿Cómo puedo hacer una enumeración "abstracta" en una biblioteca de clases .NET?

public enum ServerOperationCode : byte 
{ 
    LoginResponse = 0x00, 
    SelectionResponse = 0x01, 
    BlahBlahResponse = 0x02 
} 

public enum ClientOperationCode : byte 
{ 
    LoginRequest = 0x00, 
    SelectionRequest = 0x01, 
    BlahBlahRequest = 0x02 
} 

Eso funciona bien cuando se trabaja en su propio proyecto - se puede comparar qué miembro de la enumeración se devuelve (es decir if (packet.OperationCode == ClientOperationCode.LoginRequest)). Sin embargo, como se trata de una biblioteca de clases, el usuario deberá definir su propia enumeración.

Por lo tanto, tengo dos enumeraciones para agregar como "abstracto" - ServerOperationCode y ClientOperationCode. Sé que no es posible implementar enumeraciones abstractas en C#. ¿Cómo voy a hacer esto?

+2

no puedo averiguar lo que está tratando aquí. ¿Desea que sus códigos sean tipos .NET reales para que su cliente obtenga verificación de tipos, pero no quiere que el cliente vea los valores? ¿Qué intenta hacer que el cliente agregue una referencia a su biblioteca no resuelva? –

+1

Creo que en realidad estoy tratando de crear una biblioteca en la que los objetos estáticos puedan extenderse, lo cual es imposible. Por ejemplo, una biblioteca en la que puede anular o implementar "Server.Start", pero eso no es posible. Podría ir por los eventos, sin embargo. Veré lo que puedo hacer, gracias por todas las respuestas, las probaré. – Lazlo

Respuesta

13

Me gusta usar instancias estáticas en mis clases cuando necesito hacer esto. Se le permite tener algunos valores por defecto sino que también permite que sea extensible a través de los medios habituales de la herencia y de interfaz implementaciones:

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) { } 
    } 

asumiendo packet.OperationCode retorno un byte, es probable que tenga que aplicar un operador == para los bytes. pon este código en tu clase abstracta 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); 
} 

esto le permitirá tener el mismo control a medida que mostraron:

if (packet.OperationCode == ClientOperationCode.LoginRequest)
0

Si quiere decir que desea una enumeración que los clientes de la biblioteca puedan ampliar, consulte mi artículo de CodeProject sobre el tema, Symbols as extensible enums.

Tenga en cuenta que en mi biblioteca, Symbol elige números de identificación para los "valores enum" automáticamente, ya que está diseñado para usarse dentro de un único programa en lugar de para intercambiar valores en una red. Sin embargo, tal vez sería posible modificar Symbol.cs a su gusto para que los clientes puedan asignar valores constantes a los símbolos.

0
  1. Crear una enumeración de LoginResponse, SelectionResponse, etc., pero no especificar los valores.

  2. Have ServerOperationCode y ClientOperationCode implementan una función que, dado un bytecode entero, devuelve el valor apropiado de su Enum.

Ejemplo:

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; 
     [...] 
    } 
} 

Advertencia: desde las interfaces no pueden definir funciones estáticas, ServerOperationCode y ClientOperationCode sólo sería capaz de implementar una interfaz común si dicha función es una función de ejemplo. Si no necesitan implementar una interfaz común, GetOperationCode puede ser una función estática.

(Disculpas por cualquier C# snafus, no es mi primera lengua ...)

0

Si hay una base de datos que se comparte entre su cliente y servidor de aplicaciones, a continuación, tablas de consulta puede ayudar; la estructura de la tabla solo contiene un valor entero (ID) y una cadena (el nombre), esta tabla puede ser completada por cualquier lado de su aplicación (el cliente o el servidor) y leída por la otra. Puede almacenar en caché estas tablas (en su código) en un diccionario para una búsqueda rápida.

También puede implementar lo mismo en el archivo app.config; obligue al usuario de su biblioteca a establecer estos valores en el archivo app.config al que su biblioteca puede acceder fácilmente.

0

me escribió un mensaje de conmutación de la biblioteca con un escenario similar hace un tiempo, y yo decidido utilizar genéricos para pasar el enum definido por el usuario El problema principal con esto es que no puedes restringir tus tipos genéricos a solo enum, sino que solo puedes decir mientras T: struct. Alguien puede crear una instancia de su tipo con otro tipo primitivo (aunque el uso de ints podría seguir siendo funcional, siempre que todos sean valores únicos. El diccionario arrojará una excepción si no lo está. Posiblemente podría agregar alguna verificación adicional utilizando la reflexión para garantizar que se pasa una enumeración.

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); 
    } 
} 

Dado el escenario de ejemplo, que acabara de una subclase de esta manera.

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

¿Por qué todos piensan que enumeraciones no puede abstraerse?

La clase System.Enum IS la abstracción de una enumeración.

Puede asignar cualquier valor de enumeración a un Enum, y puede convertirlo a la enumeración original o usar el nombre o valor.

por ejemplo:

Este pequeño fragmento de código es de una colección propiedad dinámica usada en una de mis librerías de control. Me permito propiedades que se creen y se accede a través de un valor de enumeración para que sea un poco más rápido, y el error humano menos

/// <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; 
    } 

utilizo Enum.GetName(Type, object) para obtener el nombre del valor de la enumeración (que proporcione un nombre para la propiedad) y para velocidad y consistencia razones que utilizo GetHashCode() para devolver el valor entero del miembro de la enumeración (el código hash de un int es siempre sólo el valor int)

Este es un ejemplo del método que se llama:

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

Esta es EXACTAMENTE la respuesta que necesitaba aquí. Ojalá pudiera votar más esto. Esta idea me permitió crear un diccionario en una clase base abstracta que sería instanciada por varias subclases, cada una con su propia definición de enumeración. ¡Perfecto! – kmote

0

¿Qué le parece usar el diccionario estático y un método virtual para recuperar los diccionarios estáticos en las clases heredadas?

Al igual que el seguimiento de su caso:

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; 
     } 
    } 
Cuestiones relacionadas