我有一个抽象类,其中包含许多固定对象,通常是各种类型的字典和列表。然后是ReadData和WriteData的抽象方法。然后,我有两个不同的抽象类实现,一个根据“文本记录”标准写入数据,另一个根据定义的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
时,我收到错误,因为它没有new()约束,我无法返回this
因为我无法隐式转换Test.Test
至T
。
我做错了什么?
答案 0 :(得分:2)
您必须像这样更改ConvertTo<T>
方法
public T ConvertTo<T>() where T : Test, new()
然后你会得到另一个错误,因为你必须像这样投出返回值
if (x.Type.Equals(this.Type)) { return (T)this; }
答案 1 :(得分:2)
由于抽象类(和接口)不能包含有关类型构造函数的契约,因此仅指定类型T
的类型为Test
是不足以保证编译器将存在(默认/无参数)构造函数。
因此,为了保证这一点,您必须扩展泛型类型参数约束以包含该条件:
public T ConvertTo<T>() where T : Test, new()
注意new()
本质上表示“具有默认构造函数的类型”。
执行此操作后,您将遇到另一个问题,告诉您this
无法转换为T
。您必须在那里执行显式类型转换:
if (x.Type.Equals(this.Type)) { return (T)this; }
答案 2 :(得分: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;
}
}
答案 3 :(得分: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;
}
答案 4 :(得分: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); }
}
}