繁体   English   中英

使用通用转换方法在抽象类的不同实现之间进行转换

[英]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.TestT

我究竟做错了什么?

您必须像这样更改ConvertTo<T>方法

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

然后您会遇到另一个错误,因为您必须像这样强制转换返回值

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

由于抽象类(和接口)不能包含有关该类型的构造函数的协定,仅指定类型TTest类型是不足以向编译器保证将有(默认/无参数的)构造函数的。

因此,为了保证这一点,您将必须扩展泛型类型参数约束以包括该条件:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM