【问题标题】:How can I make an "abstract" enum in a .NET class library?如何在 .NET 类库中创建“抽象”枚举?
【发布时间】:2011-03-03 03:14:14
【问题描述】:

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

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

【问题讨论】:

  • 我不知道你在这里尝试什么。您希望您的代码是真正的 .NET 类型,以便您的客户端进行类型检查,但不希望客户端看到这些值?客户添加对您的库的引用无法解决的问题是什么?
  • 我想我实际上是在尝试创建一个可以扩展静态对象的库,这是不可能的。例如,您可以在其中覆盖或实现“Server.Start”的库,但这不太可能。不过,我可以按事件进行。我会看看我能做什么,谢谢所有的答案,我会试试的。

标签: c# enums abstract


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:
    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;
          [...]
        } 
    }
    

    Caveat:由于接口不能定义静态函数,ServerOperationCode 和 ClientOperationCode 只有在函数是实例函数时才能实现通用接口。如果不需要实现通用接口,GetOperationCode 可以是静态函数。

    (对任何 C# 混乱表示歉意,这不是我的第一语言...)

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

            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)

        【讨论】:

          【解决方案5】:

          不久前我写了一个类似场景的消息切换库,我决定使用泛型来传递用户定义的枚举。这样做的主要问题是您不能将泛型限制为仅枚举类型,而只能说 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
              }
          }
          

          【讨论】:

            【解决方案6】:

            为什么大家都认为枚举不能抽象?

            System.Enum 类枚举的抽象。

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

            例如:

            这个小sn-p 代码来自我的一个控件库中使用的动态属性集合。我允许通过枚举值创建和访问属性,以使其稍微快一些,减少人为错误

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

            【讨论】:

            • 这正是我需要的答案。希望我能更多地支持这个。这种洞察力使我能够在一个抽象基类中创建一个 Dictionary ,该基类将由各种子类实例化,每个子类都有自己的枚举定义。完美!
            【解决方案7】:

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

            为您的案例点赞:

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

            【讨论】:

              猜你喜欢
              • 2019-10-01
              • 1970-01-01
              • 2013-03-05
              • 1970-01-01
              • 2020-03-03
              • 1970-01-01
              • 2010-11-26
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多