![](/img/trans.png)
[英]How can I have a class that implements an interface and inherits from an abstract class? (C# .NET 3.5)
[英]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,以便客戶端可以為符號分配常量值。
為LoginResponse,SelectionResponse等創建枚舉,但不指定值。
讓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.