How can I make an “abstract” enum in a .NET class library?

后端 未结 7 371
谎友^
谎友^ 2020-12-29 10:17

I\'m making a server library in which the packet association is done by enum.

public enum ServerOperationCode : byte
{
    LoginResponse = 0x00,
    Selectio         


        
相关标签:
7条回答
  • 2020-12-29 10:29
    1. Create an Enum for LoginResponse, SelectionResponse, etc., but don't specify the values.

    2. Have ServerOperationCode and ClientOperationCode implement a function that, given an integer bytecode, returns the appropriate value from your Enum.

    Example:

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

    Caveat: since interfaces can't define static functions, ServerOperationCode and ClientOperationCode would only be able to implement a common interface if said function is an instance function. If they don't need to implement a common interface, GetOperationCode can be a static function.

    (Apologies for any C# snafus, it's not my first language...)

    0 讨论(0)
  • 2020-12-29 10:37

    Why does everyone think that Enums cannot be abstracted?

    The class System.Enum IS the abstraction of an enumeration.

    You can assign any enumeration value to an Enum, and you can cast it back to the original enumeration or use the name or value.

    eg:

    This little snippet of code is from a dynamic property collection used in one of my control libraries. I allow properties to be created and accessed through an enumeration value to make it slightly faster, and less human-error

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

    I use Enum.GetName(Type, object) to get the name of the enumeration value (to supply a name for the property), and for speed and consistency reasons I use GetHashCode() to return the integer value of the enumeration member (the hash code for an int is always just the int value)

    This is an example of the method being called:

        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 讨论(0)
  • 2020-12-29 10:41

    How about using static Dictionary and a virtual method to retrieve the static dictionaries in the inherited classes?

    Like the follow for your case:

        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;
            }
        }
    
    0 讨论(0)
  • 2020-12-29 10:43

    I like to use static instances on my classes when I need to do this. It allows you to have some default values but also lets it be extensible through the usual means of inheritance and interface implementations:

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

    assuming packet.OperationCode return a byte, you will likely have to implement an == operator for byte. put this code into your abstract OperationCode class.

    public static bool operator ==(OperationCode a, OperationCode b)
    {
      return a.Code == b.Code;
    }
    
    public static bool operator !=(OperationCode a, OperationCode b)
    {
      return !(a == b);
    }
    

    this will allow you to have the same check as you showed:

    if (packet.OperationCode == ClientOperationCode.LoginRequest)
    0 讨论(0)
  • 2020-12-29 10:45

    If you mean to say that you want an enum that can be extended by clients of the library, check out my CodeProject article on the topic, Symbols as extensible enums.

    Note that in my library, Symbol chooses ID numbers for the "enum values" automatically, since it is designed for use inside a single program rather than for exchanging values on a network. Perhaps it would be possible, however, to alter Symbol.cs to your liking so that clients can assign constant values to symbols.

    0 讨论(0)
  • 2020-12-29 10:45

    I wrote a message switching library with a similar scenario a while back, and I decided to use generics to pass the user-defined enum. The main problem with this is you can't constrain your generic to only enum types, but can only say while T: struct. Someone may instantiate your type with some other primitive type (although, using ints could still be functional, provided they're all unique values. The dictionary will throw an exception if they're not. You could possibly add some additional check using reflection to ensure you pass an enum.

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

    Given your example scenario, you'd just subclass it like this.

    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 讨论(0)
提交回复
热议问题