2010-06-13 4 views
11

Ich mache eine Server-Bibliothek, in der die Paketverknüpfung durch enum erfolgt.Wie kann ich eine "abstrakte" Enumeration in einer .NET-Klassenbibliothek erstellen?

public enum ServerOperationCode : byte 
{ 
    LoginResponse = 0x00, 
    SelectionResponse = 0x01, 
    BlahBlahResponse = 0x02 
} 

public enum ClientOperationCode : byte 
{ 
    LoginRequest = 0x00, 
    SelectionRequest = 0x01, 
    BlahBlahRequest = 0x02 
} 

Das funktioniert gut, wenn Sie in Ihrem eigenen Projekt arbeiten - Sie, welche Aufzählungsmember zurückgegeben vergleichen kann (das heißt if (packet.OperationCode == ClientOperationCode.LoginRequest)). Da es sich jedoch um eine Klassenbibliothek handelt, muss der Benutzer eine eigene Enumeration definieren.

Daher habe ich zwei enums als "abstrakte" hinzufügen - ServerOperationCode und ClientOperationCode. Ich weiß, dass es nicht möglich ist, abstrakte Enums in C# zu implementieren. Wie würde ich das machen?

+2

ich kann nicht herausfinden, was Sie hier wollen. Sie möchten, dass Ihre Codes echte .NET-Typen sind, damit Ihr Client die Typprüfung erhält, aber nicht, dass der Client die Werte sieht? Was versuchen Sie zu tun, dass der Client, der eine Referenz zu Ihrer Bibliothek hinzufügt, nicht löst? –

+1

Ich denke, ich versuche tatsächlich, eine Bibliothek zu erstellen, in der statische Objekte erweitert werden können, was unmöglich ist. Zum Beispiel eine Bibliothek, in der Sie "Server.Start" überschreiben oder implementieren können, aber das ist nicht ganz möglich. Ich könnte jedoch nach Ereignissen gehen. Ich werde sehen, was ich tun kann, danke für alle Antworten, ich werde es versuchen. – Lazlo

Antwort

13

Ich möchte statische Instanzen auf meine Klassen verwenden, wenn ich dies tun muss. Es ermöglicht Ihnen, einige Standardwerte haben, sondern auch ermöglicht es durch die üblichen Mittel der Vererbung und Interface-Implementierungen erweiterbar sein:

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) { } 
    } 

Annahme packet.OperationCode Rückkehr ein Byte, werden Sie wahrscheinlich einen Operator == für Byte implementieren. Setzen Sie diesen Code in Ihre abstrakte OperationCode-Klasse.

public static bool operator ==(OperationCode a, OperationCode b) 
{ 
    return a.Code == b.Code; 
} 

public static bool operator !=(OperationCode a, OperationCode b) 
{ 
    return !(a == b); 
} 

dies ermöglicht es Ihnen, die gleiche Kontrolle haben, wie Sie zeigte:

if (packet.OperationCode == ClientOperationCode.LoginRequest)
0

Wenn Sie sagen wollen, dass Sie eine Enumeration möchten, die von Clients der Bibliothek erweitert werden kann, finden Sie in meinem CodeProject Artikel zum Thema, .

Beachten Sie, dass Symbol in meiner Bibliothek ID-Nummern automatisch für die "Enum-Werte" auswählt, da es für die Verwendung innerhalb eines einzelnen Programms und nicht für den Austausch von Werten in einem Netzwerk konzipiert ist. Vielleicht ist es jedoch möglich, Symbol.cs nach Belieben zu ändern, damit Clients Symbolen Symbole konstante Werte zuweisen können.

0
  1. Eine Enum für Login, SelectionResponse, etc., aber do nicht die Werte angeben.

  2. Haben ServerOperationCode und ClientOperationCode eine Funktion implementiert, die bei einem Integer-Bytecode den entsprechenden Wert aus Ihrer Enum zurückgibt.

Beispiel:

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: da Schnittstellen keine statischen Funktionen definieren, und ServerOperationCode ClientOperationCode würde nur in der Lage sein, eine gemeinsame Schnittstelle zu implementieren, wenn die Funktion eine Instanz-Funktion ist. Wenn sie keine gemeinsame Schnittstelle implementieren müssen, kann GetOperationCode eine statische Funktion sein.

(entschuldigen uns für die C# snafus, es ist nicht meine erste Sprache ...)

0

Wenn es eine Datenbank, die zwischen Client und Server-Anwendung gemeinsam genutzt wird, dann Nachschlagetabellen helfen können; Die Tabellenstruktur enthält nur einen ganzzahligen Wert (ID) und eine Zeichenfolge (den Namen). Diese Tabelle kann von beiden Seiten Ihrer Anwendung (dem Client oder dem Server) ausgefüllt und von der anderen Seite gelesen werden. Sie können diese Tabelle (in Ihrem Code) zum schnellen Nachschlagen in einem Wörterbuch zwischenspeichern.

Sie können das gleiche auch in der app.config-Datei implementieren; Fordern Sie den Benutzer Ihrer Bibliothek auf, diese Werte in der Datei app.config festzulegen, auf die Ihre Bibliothek problemlos zugreifen kann.

0

Ich schrieb eine Nachricht mit einem ähnlichen Szenario Schalt Bibliothek eine Weile zurück, und ich beschlossen, Generika zu verwenden, die passieren benutzerdefinierte Enumeration Das Hauptproblem dabei ist, dass Sie Ihr generic nicht nur auf enum-Typen beschränken können, sondern nur während T: struct sagen können. Jemand kann Ihren Typ mit einem anderen primitiven Typ instanziieren (obwohl Ints trotzdem funktionieren könnten, vorausgesetzt, dass sie alle eindeutige Werte sind. Das Wörterbuch wird eine Ausnahme auslösen, wenn sie nicht vorhanden sind.) sicherzustellen, dass Sie eine eNUM passieren.

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

Ihr Beispiel gegebenen Szenario Sie es gerade wie dieser Unterklasse würde.

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

Warum jeder denkt, dass Aufzählungen nicht abstrahiert werden kann?

Die Klasse System.Enum IS die Abstraktion einer Aufzählung.

Sie können einer Enum einen beliebigen Enumerationswert zuweisen und ihn in die ursprüngliche Enumeration umwandeln oder den Namen oder Wert verwenden.

zB:

Dieser kleine Code-Snippet ist von einer dynamischen Eigenschaft Sammlung in einem meiner Control-Bibliotheken verwendet. Ich erlaube Eigenschaften erstellt und durch einen Aufzählungswert zugegriffen werden, um es schneller leicht zu machen, und weniger Menschen Fehler

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

ich Enum.GetName(Type, object) verwenden den Namen des Aufzählungswert zu erhalten (einen Namen für die Eigenschaft zu liefern) und für die Geschwindigkeit und Konsistenzgründen I GetHashCode() verwenden den ganzzahligen Wert des Enumerationsmember zurückzukehren (der Hash-Code für ein int ist immer nur der int value)

Dies ist ein Beispiel des Verfahrens ist, genannt werden:

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); 
    } 
+0

Das ist GENAU die Antwort, die ich hier brauchte. Ich wünschte, ich könnte das noch verbessern. Diese Einsicht ermöglichte mir, ein Dictionary in einer abstrakten Basisklasse zu erstellen, die von verschiedenen Unterklassen mit jeweils eigenen Enum-Definitionen instanziiert würde. Perfekt! – kmote

0

Wie wäre es mit static Dictionary und einer virtuellen Methode zum Abrufen der statischen Wörterbücher in den geerbten Klassen?

Wie die Folge für Ihren Fall:

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