2017-03-16 35 views
1

我有一個抽象類,其中包含許多固定對象,通常是各種類型的字典和列表。然後有ReadData和WriteData的抽象方法。然後,我有兩種不同的抽象類實現,一種是根據「文本記錄」標準寫入數據,另一種是根據定義的XML模式寫入其他XML。使用泛型轉換方法在抽象類的不同實現之間轉換

因此,除了不同的讀寫方式外,這兩種實現方式是相同的。

我現在想要做的是在數據讀取格式1中,然後把它寫成格式2.我可以通過編寫方法,如.ToFormat2()和在各自所屬類別.ToFormat1(),大概.FromFormat2()和做到這一點很容易.FromFormat1()如果我想要完整性。但是這些例程本質上是一樣的,而當我需要格式3並且不希望在每一箇中快樂地實現兩個或多個相同的「到」方法時,我正在考慮未來(不太遙遠)的未來。類。這是浪費時間,很難調試/改變,只是不好。

所以我一直在試圖在抽象類中編寫一個通用轉換器。下面的代碼說明了我到目前爲止所做的原理:

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()約束,並且我不能返回this,因爲我無法隱式地將Test.Test轉換爲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()這實際上是說「有一個默認的構造函數的類型」。


這樣做之後,你會碰上,告訴您this不能轉換爲T另一個問題。你將不得不進行顯式類型轉換有:

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

你必須改變這樣的

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

ConvertTo<T>方法,然後因爲你要投這樣

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

編輯:如果您不想/不能有參數構造函數,你可以單獨從數據(字典和列表)這樣的讀/寫方法:

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

這種link也許幫助您: 應用新的約束類型參數時,您的泛型類創建類型的新實例,當您使用其他約束new()約束,它必須最後

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的類中的所有有趣的位,然後將它放入界面中。每個類的構造函數允許我在一個重載中傳遞一個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); } 
    } 
}