[英]Converting between different implementations of abstract class with generic convert method
我有一个抽象类,其中包含许多固定对象,通常是字典和各种类型的列表。 然后有用于ReadData和WriteData的抽象方法。 然后,我有这个抽象类的两种不同实现,一种实现是根据“文本记录”标准写入数据,另一种是根据定义的XML模式写入XML。
因此,这两种实现方式是相同的,只是读写方式不同。
我现在想做的是读取格式1的数据,然后将其写入格式2。我可以通过在相应的类中编写诸如.ToFormat2()
和.ToFormat1()
之类的方法来轻松实现此目的.FromFormat2()
和.FromFormat1()
如果我想完整。 但是这些例程本质上是相同的,当我需要格式3时,我正在考虑(不太遥远)未来,并且不想在每个方法中都快乐地实现两个或更多相同的“ 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
,因为它没有新的()的约束,我不能回到this
,因为我不能隐式转换Test.Test
到T
。
我究竟做错了什么?
您必须像这样更改ConvertTo<T>
方法
public T ConvertTo<T>() where T : Test, new()
然后您会遇到另一个错误,因为您必须像这样强制转换返回值
if (x.Type.Equals(this.Type)) { return (T)this; }
由于抽象类(和接口)不能包含有关该类型的构造函数的协定,仅指定类型T
为Test
类型是不足以向编译器保证将有(默认/无参数的)构造函数的。
因此,为了保证这一点,您将必须扩展泛型类型参数约束以包括该条件:
public T ConvertTo<T>() where T : Test, new()
注意new()
本质上说“具有默认构造函数的类型”。
完成此操作后,您将遇到另一个问题, this
问题告诉您无法将其转换为T
您将必须在此处执行显式类型转换:
if (x.Type.Equals(this.Type)) { return (T)this; }
您可以要求所有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;
}
编辑:如果您不希望/不能有无参数的构造函数,则可以将Read / Write方法与数据(字典和列表)分开,如下所示:
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;
}
}
该链接可能帮助您:当泛型类创建类型的新实例时,将新约束应用于类型参数;当将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;
}
我将@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); }
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.