12

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

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

4

7 回答 7

14

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

    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)
于 2010-06-13T03:23:13.253 回答
8

为什么大家都认为 Enums 不能抽象呢?

System.Enum 类枚举的抽象。

您可以将任何枚举值分配给 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);
    }
于 2013-05-24T00:26:22.923 回答
0

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

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

于 2010-06-13T03:03:31.893 回答
0
  1. 为 LoginResponse、SelectionResponse 等创建一个枚举,但不要指定值。

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

例子:

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

于 2010-06-13T03:10:26.103 回答
0

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

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

于 2010-06-13T03:20:17.440 回答
0

不久前我写了一个类似场景的消息切换库,我决定使用泛型来传递用户定义的枚举。这样做的主要问题是您不能将泛型限制为仅枚举类型,而只能说 while T: struct. 有人可能会用其他一些原始类型实例化您的类型(尽管使用 int 仍然可以正常工作,前提是它们都是唯一值。如果不是,字典将抛出异常。您可以使用反射添加一些额外的检查确保您通过枚举。

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
    }
}
于 2010-06-13T10:21:12.273 回答
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;
        }
    }
于 2014-01-21T06:13:09.227 回答