2017-03-16 12 views
1

私は、多くの固定オブジェクト、一般的にはさまざまなタイプの辞書とリストを含む抽象クラスを持っています。 ReadDataとWriteDataの抽象メソッドが存在します。次に、この抽象クラスの2つの異なる実装を用意しています.1つは、「テキストレコード」標準に従ってデータを書き出し、もう1つは定義されたXMLスキーマに対してデータを書き込みます。汎用変換メソッドを持つ抽象クラスの異なる実装間の変換

したがって、2つの実装は、異なる読み書き方法を除いて同一です。私は今、何をしたいか

はフォーマット1のデータを読み込み、その後、フォーマット2としてそれを書くことで、私はそれぞれのクラスでは、このような.ToFormat2().ToFormat1()などのメソッドを書き込むことによって、非常に簡単にこれを行うには、おそらく.FromFormat2()ことができ、完全性が必要な場合は.FromFormat1()。しかし、これらのルーチンは本質的に同じであり、私はフォーマット3が必要になるときに(あまりにも遠くない)将来を考えており、それぞれに2つ以上の同一の「To」メソッドを有益に実装する必要はありませんクラス。それは時間の無駄です、デバッグ/変更するのが難しく、ちょうど良いことではありません。

私は抽象クラス内に汎用コンバータを記述しようとしています。次のコードは、私がこれまで行ってきたことの原則を示しています:

public abstract class Test 
{ 
    public string Type; 
    public Dictionary<string, string> Dic1; 
    public Dictionary<string, int> Dic2; 

    public abstract void Read(string fileName); 
    public abstract void Write(string fileName); 

    public T ConvertTo<T>() where T : Test 
    { 
     T x = new T(); 
     if (x.Type.Equals(this.Type)) { return this; } 
     x.Dic1 = this.Dic1; 
     x.Dic2 = this.Dic2; 
     return x; 
    } 
} 

public class Format1 : Test 
{ 
    // Constructor 
    public Format1() { Type = "Format1"; Dic1 = new Dictionary<string, string>(); Dic2 = new Dictionary<string, int>(); } 

    // Concrete implementations of abstract Read and Write for "Format1" 
    public override void Read(string fileName) { /* do reading stuff */ } 
    public override void Write(string fileName) { /* do writing stuff */ } 
} 

public class Format2 : Test 
{ 
    // Constructor 
    public Format2() { Type = "Format2"; Dic1 = new Dictionary<string, string>(); Dic2 = new Dictionary<string, int>(); } 

    // Concrete implementations of abstract Read and Write for "Format2" 
    public override void Read(string fileName) { /* do reading stuff */ } 
    public override void Write(string fileName) { /* do writing stuff */ } 
} 

コンパイラはこれを好まないのです。 xを新しいTと宣言したときにエラーが発生しました。なぜなら、new()制約がなく、を返すことができないからです。Test.TestTに暗黙にキャストできないためです。

私は間違っていますか?あなたはこの

public T ConvertTo<T>() where T : Test, new() 

のようなあなたのConvertTo<T>方法を変更する必要が

+1

代わりに、これに対して継承を使用する代わりにオブジェクトのラッパーを作成することを検討します。クラスが本当にロード/セーブされているかを気にするべきではないと考えてください(単一責任の原則)。代わりにSave()メソッドとLoad()メソッドを持つITestPersistanceを作成した場合は、継承を乱用することなくここにすべてのロジックを含めることができます。つまり、他のコードを変更することなく、ITestPersistance(つまりFormat3TestPersistance)の新しい実装を追加することができます。これはITestFormatとITestPersistanceに拡張できます。 – Milney

+0

@Milney。ええ、はい。あなたはそれを指摘しています。そこの遺産で少し失われた! – StuartR143

答えて

2

のように戻り値をキャストする必要があるためその後、あなたは私が推測する別のエラーが発生しますおよびインターフェイス)は、型のコンストラクタに関するコントラクトを含めることはできません。タイプTがタイプTestであることを指定するだけでは、(デフォルト/パラメータなしの)コンストラクタが存在することをコンパイラに保証するには不十分です。

public T ConvertTo<T>() where T : Test, new() 

注意基本的に「デフォルトコンストラクタを持っているタイプの」言うnew()

だからそれを保証するために、あなたはその条件を含めるようにジェネリック型引数の制約を拡張する必要があります。


ことをやった後、あなたはthisTに変換できないことを示しています別の問題に実行されます。あなたはそこに明示的な型変換を実行する必要があります。

if (x.Type.Equals(this.Type)) { return (T)this; } 
2

あなたは抽象クラス(以降、この

if (x.Type.Equals(this.Type)) { return (T)this; } 
0

新しいインスタンスを作成することができるように、あなたは、パラメータなしのコンストラクタを持っているすべてのTestのクラスを必要とすることができます

public T ConvertTo<T>() where T : Test, new() 
{ 
    T x = new T(); 
    if (x.Type.Equals(this.Type)) { return (T) this; } 
    x.Dic1 = this.Dic1; 
    x.Dic2 = this.Dic2; 
    return x; 
} 

EDIT:あなたの場合したくない/あなたは、このようなデータからのリード/ライト方法(辞書やリスト)を分離することができ、パラメータなしのコンストラクタを持つことはできません。

public interface IReadWriter 
{ 
    void Read(Test test, string filenName); 
    void Write(Test test, string filenName); 
} 

public class Test 
{ 
    public string Type; 
    public Dictionary<string, string> Dic1; 
    public Dictionary<string, int> Dic2; 
    public IReadWriter readWriter; 

    public Test(IReadWriter readWriter) 
    { 
     this.readWriter = readWriter; 
    } 

    public void Read(string fileName) 
    { 
     readWriter.Read(this, fileName); 
    } 

    public void Write(string fileName) 
    { 
     readWriter.Write(this, fileName); 
    } 

    public Test WithReadWriter(IReadWriter other) 
    { 
     Test x = new Test(other); 
     //if (x.Type.Equals(this.Type)) { return this; } 
     x.Dic1 = this.Dic1; 
     x.Dic2 = this.Dic2; 
     return x; 
    } 
} 
+0

私はコンストラクタを設定した保護されたものをいくつか変更するメソッドを追加することで、パラメータのないコンストラクタを持つように少し変更することができますが、ここでは少しわかります。長期的にはより良いかもしれない感覚。 – StuartR143

0

このは、多分あなたを助ける:私はあなたのジェネリッククラスは型の新しいインスタンスを作成するとき typeパラメータに新しい制約を適用し、あなたが他の制約を持つ新しい()制約を使用する場合、それは最後の

public T ConvertTo<T>() where T : Test,new() 
    { 
     T x = new T(); 
     if (x.Type.Equals(this.Type)) { return (this as T) ; } 
     x.Dic1 = this.Dic1; 
     x.Dic2 = this.Dic2; 
     return x; 
    } 
0

を指定する必要があります彼は一般的なコンバータについて質問しているという質問に答えたので、答えとして「@ poke」とマークしています。しかし、私は@Milneyから、私が実際に継承すべきではないものを継承していたことを指摘するコメントを取った。だからこれはおそらく私が投稿しているのと同じことをするためのより良い方法です。

これは、DataModelと呼ばれるクラス内のすべての興味深いビットを持ち、これがインターフェイスに配置されます。各クラスのコンストラクターによって、過負荷の1つにDataModelを渡すことができます。これにより、前のクラスのすべてのデータに基づいて新しいフォーマットを簡単に作成できます。

class Program 
{ 
    static void Main(string[] args) 
    { 
     Format1 f1 = new Format1("5.10"); 
     f1.Data.Dic1.Add("Greet", "Hello World"); 
     f1.Data.Dic2.Add("RepeatGreet", 10); 
     f1.Write("f1"); 
     Console.WriteLine("-------------------------------------------------------"); 

     Format2 f2 = new Format2("2.1","general",f1.Data); 
     f2.Data.Dic1.Add("Goodbye", "See you later, Alligator"); 
     f2.Data.Dic2.Add("RepeatBye", 1); 
     f1.Write("f1"); 
     f2.Write("f2"); 

     Console.ReadKey(); 
    } 
} 

public interface IDataFormat 
{ 
    void Read(string filename); 
    void Write(string filename); 
    string Type { get; } 
    string Version { get; } 
    DataModel Data { get; } 
} 

public class DataModel 
{ 
    public Dictionary<string, string> Dic1; 
    public Dictionary<string, int> Dic2; 

    // Constructor 
    public DataModel() { Dic1 = new Dictionary<string, string>(); Dic2 = new Dictionary<string, int>(); } 
} 

public class Format1 : IDataFormat 
{ 
    public string Type { get; } 
    public string Version { get; } 
    public DataModel Data {get; } 

    // Constructors 
    public Format1(string version) : this(version, new DataModel()) { } 
    public Format1(string version, DataModel data) { Type = "Format1"; Version = version; Data = data; } 

    // Concrete implementations of abstract Read and Write for "Format1" 
    public void Read(string fileName) { /* do reading stuff */ } 
    public void Write(string fileName) 
    { 
     Console.WriteLine("WRITING " + fileName +" IN FORMAT1:"); 
     Console.WriteLine("Type: " + Type + "\tVersion: " + Version); 
     foreach (KeyValuePair<string, string> kvp in Data.Dic1) { Console.WriteLine("\t" + kvp.Key + "\t" + kvp.Value); } 
     foreach (KeyValuePair<string, int> kvp in Data.Dic2) { Console.WriteLine("\t" + kvp.Key + "\t" + kvp.Value); } 
    } 
} 

public class Format2 : IDataFormat 
{ 
    // Properties 
    public string Type { get; } 
    public string SubType { get; set; }  // A property unique to this class 
    public string Version { get; } 
    public DataModel Data { get; } 

    // Constructors. 
    // Including a constructor which is unique to this class because it uses a unique property of this class 
    public Format2(string version) : this(version, "", new DataModel()) { } 
    public Format2(string version, DataModel data) : this(version, "", data) { } 
    public Format2(string version, string subType, DataModel data) { Type = "Format2"; Version = version; SubType = subType; Data = data; } 

    // Concrete implementations of abstract Read and Write for "Format2" 
    public void Read(string fileName) { /* do reading stuff */ } 
    public void Write(string fileName) 
    { 
     Console.WriteLine("WRITING " + fileName + " IN FORMAT2:"); 
     Console.WriteLine("Type: " + Type + "........Version: " + Version); 
     foreach (KeyValuePair<string, string> kvp in Data.Dic1) { Console.WriteLine("........" + kvp.Key + "........" + kvp.Value); } 
     foreach (KeyValuePair<string, int> kvp in Data.Dic2) { Console.WriteLine("........" + kvp.Key + "........" + kvp.Value); } 
    } 
} 
関連する問題