2017-03-16 32 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); } 
    } 
}