如何在.NET类库中创建“抽象”枚举?

时间:2010-06-13 02:59:53

标签: c# enums abstract

我正在创建一个服务器库,其中数据包关联由枚举完成。

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#中实现抽象枚举是不可能的。我该怎么做?

7 个答案:

答案 0 :(得分: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)

答案 1 :(得分:5)

为什么每个人都认为Enums不能被抽象出来?

类System.Enum IS 枚举的抽象。

您可以将任何枚举值分配给枚举,然后将其强制转换回原始枚举或使用名称或值。

例如:

这段代码片段来自我的一个控件库中使用的动态属性集合。我允许通过枚举值创建和访问属性,使其更快,更少人为错误

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

答案 2 :(得分:0)

如果您想要一个可以由库的客户扩展的枚举,请查看我的CodeProject主题Symbols as extensible enums上的文章。

请注意,在我的库中,Symbol会自动为“枚举值”选择ID号,因为它设计用于单个程序内而不是用于在网络上交换值。但是,也许可以根据自己的喜好更改Symbol.cs,以便客户端可以为符号分配常量值。

答案 3 :(得分:0)

  1. 为LoginResponse,SelectionResponse等创建枚举,但指定值。

  2. 让ServerOperationCode和ClientOperationCode实现一个函数,给定整数字节码,从Enum返回适当的值。

  3. 示例:

    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#snafus道歉,这不是我的第一语言......)

答案 4 :(得分:0)

如果您的客户端和服务器应用程序之间共享了一个数据库,那么查找表可能有所帮助;表结构只包含一个整数值(ID)和一个字符串(名称),该表可以由应用程序的任何一方(客户端或服务器)填写,并由另一方读取。您可以将这些表(在您的代码中)缓存在字典中以便快速查找。

您也可以在app.config文件中实现相同的功能;强制您的库的用户在您的库可以轻松访问的app.config文件中设置这些值。

答案 5 :(得分:0)

我曾经写过一个类似场景的消息切换库,我决定使用泛型来传递用户定义的枚举。这个问题的主要问题是你不能将你的泛型约束到枚举类型,但只能说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
    }
}

答案 6 :(得分:0)

如何使用静态Dictionary和虚方法来检索继承类中的静态字典?

就像您的案例的跟随:

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