簡體   English   中英

如何在.NET類庫中創建“抽象”枚舉?

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

我正在創建一個服務器庫,其中數據包關聯由枚舉完成。

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#中實現抽象枚舉是不可能的。 我該怎么做呢?

當我需要這樣做時,我喜歡在我的類上使用靜態實例。 它允許您擁有一些默認值,但也可以通過常規的繼承和接口實現方式進行擴展:

    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)

為什么每個人都認為Enums不能抽象?

類System.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);
    }

如何使用靜態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;
        }
    }

如果您想要一個可以由庫的客戶端擴展的枚舉,請查看我的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文件中實現相同的功能; 強制您的庫的用戶在您的庫可以輕松訪問的app.config文件中設置這些值。

我曾經寫過一個類似場景的消息切換庫,我決定使用泛型來傳遞用戶定義的枚舉。 這個問題的主要問題是你不能將你的泛型約束到枚舉類型,但只能說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
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM