2017-11-14 15 views
3

私は機能的なパッケージパーサーを構築しようとしています。 specficため静的ファクトリを使用する代わりに(抽象的な静的メソッドを持たないように)

(!このクラスは抽象的ではない、編集上の注意)

public class Datagram 
{ 
    public abstract static Datagram CreateFromDatagram(Datagram datagram); 
} 

そして:私は今、私は単純に私はそれがこのように定義されているだろうと想像して、基本クラスDatagramを持っていますデータグラムは、exampeとして、イーサネットとTCPを言う:

public class EthernetDatagram : Datagram, IPayloadDatagram 
{ 
    public override static Datagram CreateFromDatagram(Datagram datagram) 
    { 
     return new EthernetDatagram(); 
    } 

    public Datagram Payload { get; } 
} 

public class TcpDatagram : Datagram, IPayloadDatagram 
{ 
    public overrides static Datagram CreateFromDatagram(Datagram datagram) 
    { 
     return new TcpDatagram(); 
    } 

    public Datagram Payload { get; } 
} 

(不可能)この理由抽象静的メソッドは、私は私が「連鎖」これらすべてのパケット一緒にすることを可能にする拡張メソッドを持つようにしたいです。

public static class DatagramExtensions 
{ 
    public static T As<T>(this IPayloadDatagram datagram) where T : Datagram 
    { 
     return (T)T.CreateFromDatagram(datagram.Payload); 
    } 
} 

だから、私は完全に新しいデータグラムタイプANewDatagramを持って行う必要があるだろうすべては、それがファクトリメソッドCreateFromDatagramだ定義持っている、と私はその後、陽気に私の機能拡張を使用できるようになります:

SomeDatagram.As<EthernetDatagram>().As<TcpDatagram>().As<ANewDatagram>()... 

これはすべて拡張可能です。

私は抽象クラスを継承することができないので、これはうまくいきませんが、このような汎用クラスをインスタンス化する良い方法は何でしょうか?

私は反射を使うことができましたが、それはユーザーから隠れています。 ANewDatagramを作成しようとすると、あとでCreateFromDatagramメソッドが反映されていることを覚えておく必要があります。

私は現在、リフレクションを使用してコンストラクタを取得していますが、ペイロードを取得する特定のコンストラクタがあることを強制する方法はありません。誰かが新しいDatagramを作成した場合、適切なコンストラクタが追加されているという保証はありません。私はコメントの中でこれを知らせなければなりません。失敗する可能性は非常に低く、実行時には可能な限り最新の時点です。

アーキテクチャ上、またはこの問題を回避できる何らかの形のインターフェイス/継承がありますか?

+0

あるデータグラムから別のデータグラムへの変換に制約がありますか?だから、他のデータグラムに変換することはできますか? – ckruczek

+0

私はそう信じていませんが、それらはすべてバイト配列から構築されています(またはそれらの配列である必要があります)。その配列を解釈することで自分自身を定義します - それで、一方をもう一方に変換するには、バイト。しかし、同じ基本データを持つ新しいオブジェクトを作成するだけではなく、どのように変換するのか分かりません。再び、コンストラクターの問題が発生します。 – Joe

答えて

3

は、私は別のソリューションをお勧めします。あなたが他の方法で回避行く。このソリューションにより

public class Datagram 
{ 
    public byte[] Data { get; set; } 
} 


public interface IPayload 
{ 
    Datagram Payload { get; } 

}  

public interface IConvertible 
{ 
    IPayload Convert(IPayload load); 
} 

public class EthernetDatagram : IPayload , IConvertible 
{ 
    public Datagram Payload 
    { 
     get 
     { 
      return null; 
     } 
    } 


    public IPayload Convert(IPayload load) 
    { 
     return new EthernetDatagram(); 
    } 
} 

public class TcpDatagram : IConvertible, IPayload 
{ 
    public Datagram Payload 
    { 
     get 
     { 
      return null; 
     } 
    } 

    public IPayload Convert(IPayload load) 
    { 
     return null; 
    } 
} 

public static class Extension 
{ 
    public static IPayload As<T>(this IPayload load) where T : class, IConvertible, new() 
    { 
     IConvertible conv = new T(); 
     return conv.Convert(load); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     IPayload load = new TcpDatagram(); 

     var result = load.As<EthernetDatagram>(); 
    } 
} 

:私は、新しいソリューションとDepdency Inversion Principle

アップデートを適用します。ハードリフレクションのパスを離れ、コンバートしたいコンクリートタイプを渡すことで抽象化の別のレイヤーに移動し、完全にコントロールできます。新しいデータグラムのためにあなたがしなければならないのは、それらの間の変換を望む限り、両方のインターフェースを実装することだけです。 これはあなたの問題により適していますか?コメントへの

更新:

新しいソリューションは、かつての解決策の欠点を克服します。 ジェネリックパラメータによる '変換'タイプのリフレクションと指定はありません。あなたは、あなたが望むものに正確に到達するために、 'As'に連鎖することができます。 あなたが今提供しなければならないのは、インターフェイスの実装、デフォルトのctorとthatsです。

+0

[チャットでこのディスカッションを続行しましょう](http://chat.stackoverflow.com/rooms/158974/discussion-between-ckruczek-and-joe) – ckruczek

3
(誰もが私が働いている完全なソースコードを見たい場合は、私は可能な限り小さな変更で、 https://github.com/PcapDotNet/Pcap.Netの一環として、パケット解釈ライブラリにこれらの拡張機能を追加しようとしています)

...しかし、ペイロードを受け取る特定の コンストラクタがあることを強制する方法はありません。

基本抽象クラスに適切なコンストラクタを宣言することによって、それを強制できます。

また、コードにいくつかの変更を提案します。すべての派生クラスは同じPayloadプロパティを持つことになっているため、クラスDatagramクラスに宣言します。またDatagramクラスをIPayloadDatagramインターフェイスとして実装することを検討してください。派生した各クラスをこのインタフェースを実装するものとしてマークする必要はありません。

はここでうまくいけば、あなたが必要なものを行い、そのサンプルコードです:

public interface IPayloadDatagram 
{ 
    Datagram Payload { get; } 
} 

public abstract class Datagram : IPayloadDatagram 
{ 
    public Datagram Payload { get; } 

    protected Datagram(Datagram datagram) 
    { 
     Payload = datagram; 
    } 
} 

public class EthernetDatagram : Datagram 
{ 
    public EthernetDatagram(Datagram datagram) : base(datagram) 
    { 
    } 
} 

public static class DatagramExtensions 
{ 
    public static T As<T>(this IPayloadDatagram datagram) where T : Datagram 
    { 
     return (T)Activator.CreateInstance(typeof(T), datagram.Payload); 
    } 
} 
+0

これはコンストラクタを強制しませんが、 'public EthernetDatagram():base(new Datagram())'は許可されています。あるいは、 'public EthernetDatagram(データグラムデータグラム、文字列名):ベース(データグラム){_name =名前;両方で反射が失敗する。それはコンストラクタが 'base'を参照する限りコンパイルされますが、任意の引数を持つことができます – Joe

+0

' Payload'のない 'Datagrams'があります。私は最初に 'IPayloadDatagram'を持っていると思っていましたが、私はそれをやっていない何らかの理由があったと確信しています(おそらくこれをすべて動作させようとしています)。私は継承よりもインターフェイスがはるかに快適です。 – Joe

0

静的メソッドが1つしかなく、そのメソッドが実装定義アクションを参照している場合はどうなりますか?

これはコンパイル時のチェックを追加するので、ユーザーは少なくとも変換に気づく必要があります。

public interface IPayloadDatagram 
{ 
    Datagram Payload { get; } 
} 

public abstract class Datagram 
{ 
    public static Datagram CreateFromDatagram(Datagram datagram) 
    { 
     var action = datagram.GetConverter(); 
     return action(datagram); 
    } 

    protected abstract Func<Datagram, Datagram> GetConverter(); 
} 

public class EthernetDatagram : Datagram, IPayloadDatagram 
{ 
    protected override Func<Datagram, Datagram> GetConverter() 
    { 
     return x => new EthernetDatagram(); 
    } 

    public Datagram Payload { get; set; } 
} 

public class TcpDatagram : Datagram, IPayloadDatagram 
{ 
    protected override Func<Datagram, Datagram> GetConverter() 
    { 
     return x => new TcpDatagram(); 
    } 

    public Datagram Payload { get; set; } 
} 

public static class DatagramExtensions 
{ 
    public static T As<T>(this IPayloadDatagram datagram) where T : Datagram 
    { 
     return (T)Datagram.CreateFromDatagram(datagram.Payload); 
    } 
} 
+0

私は同じようなもので遊んでいましたが、決してそれを働かせることはできませんでした。私の問題は、 '(T)Datagram.CreateFromDatagram(datagram.Payload)'のdatagram.PayloadはT型ではないということです(これは単なる 'ジェネリック'の 'Datagram'です。それで 'GetConverter'はいつも普通の' Datagram'のバージョンです。混乱させて申し訳ありません。 ペイロードのタイプがTの場合は、 'As '拡張子を持つ必要はありません。単に 'Payload'プロパティを使うだけです。 – Joe

関連する問題