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

当我需要这样做时,我喜欢在我的类上使用静态实例。 它允许您拥有一些默认值,但也可以通过常规的inheritance和接口实现方式进行扩展:

  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) 

为什么每个人都认为Enum不能被抽象?

System.Enum类是枚举的抽象。

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

例如:

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

  ///  /// creates a new trigger property. ///  ///  ///  ///  ///  protected virtual TriggerProperty Create(T value, Enum name) { var pt = new TriggerProperty(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(1, Props.A); Create(1, Props.B); Create(1, Props.C); Create(Color.Gray, Props.Color); Create(GradientType.Linear, Props.Type); Create(RectangleF.Empty, Props.Region); Create(PointF.Empty, Props.Centre); Create(0f, Props.Angle); } 

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

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

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

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

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

我曾经写过一个带有类似场景的消息切换库,我决定使用generics来传递用户定义的枚举。 这个问题的主要问题是你不能将你的generics约束到枚举类型,但只能说T:struct。 有人可以使用其他一些原始类型来实例化你的类型(尽管使用int仍然可以起作用,前提是它们都是唯一值。如果它们不是,字典将抛出exception。你可以使用reflection添加一些额外的检查确保你通过枚举。

 public abstract class DefaultMessageHandler : IMessageHandler where T : struct { public delegate void MessageHandlerDelegate(IMessage message, IConnection connnection); private readonly IDictionary messageHandlerDictionary = new Dictionary(); 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 message, IConnection connection) { } void IMessageHandler.HandleMessage(IMessage 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 { public ServerOperationHandler() { this.RegisterMessageHandler(ServerOperationCode.LoginResponse, this.HandleLoginResponse); this.RegisterMessageHandler(ServerOperationCode.SelectionResponse, this.HandleSelectionResponse); } private void HandleLoginResponse(IMessage message, IConnection connection) { //TODO } private void HandleSelectionResponse(IMessage message, IConnection connection) { //TODO } } 

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

就像您的案例的跟随:

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