帮我“干掉”这个.net XML序列化代码

时间:2010-02-08 17:50:00

标签: c# refactoring xml-serialization dry

我有一个基本集合类和一个子集合类,每个类都是可序列化的。在测试中,我发现只是让孩子班的ReadXml方法调用base.ReadXml后来导致了InvalidCastException。首先,这是类结构:

基类

// Collection of Row objects
[Serializable]
[XmlRoot("Rows")]
public class Rows : IList<Row>, ICollection<Row>, IEnumerable<Row>,
    IEquatable<Rows>, IXmlSerializable
{
    public Collection<Row> Collection { get; protected set; }

    public void ReadXml(XmlReader reader)
    {
        reader.ReadToFollowing(XmlNodeName);
        do
        {
            using (XmlReader rowReader = reader.ReadSubtree())
            {
                var row = new Row();
                row.ReadXml(rowReader);
                Collection.Add(row);
            }
        } while (reader.ReadToNextSibling(XmlNodeName));
    }
}

派生类

// Acts as a collection of SpecificRow objects, which inherit from Row.  Uses the same
// Collection<Row> that Rows defines which is fine since SpecificRow : Row.
[Serializable]
[XmlRoot("MySpecificRowList")]
public class SpecificRows : Rows, IXmlSerializable
{
    public new void ReadXml(XmlReader reader)
    {
        // Trying to just do base.ReadXml(reader) causes a cast exception later
        reader.ReadToFollowing(XmlNodeName);
        do
        {
            using (XmlReader rowReader = reader.ReadSubtree())
            {
                var row = new SpecificRow();
                row.ReadXml(rowReader);
                Collection.Add(row);
            }
        } while (reader.ReadToNextSibling(XmlNodeName));
    }

    public new Row this[int index]
    {
        // The cast in this getter is what causes InvalidCastException if I try
        // to call base.ReadXml from this class's ReadXml
        get { return (Row)Collection[index]; }
        set { Collection[index] = value; }
    }
}

这是导致运行时的代码InvalidCastException 如果我没有使用上面ReadXml中显示的SpecificRows版本(即,如果我只是我得到例外)从base.ReadXml内拨打SpecificRows.ReadXml

TextReader reader = new StringReader(serializedResultStr);
SpecificRows deserializedResults = (SpecificRows)xs.Deserialize(reader);
SpecificRow = deserializedResults[0]; // this throws InvalidCastException

因此,上面的代码编译并运行无异常,但是我认为Rows.ReadXmlSpecificRows.ReadXml本质上是相同的代码。 XmlNodeNamenew Row() / new SpecificRow()的值是差异。您如何建议我提取ReadXml两个版本的所有常用功能?为一个方法创建一些泛型类是否愚蠢?对于冗长的代码示例感到抱歉,我只是想提供一个原因,我不能简单地在base.ReadXml内拨打SpecificRows

3 个答案:

答案 0 :(得分:1)

看起来唯一的区别就是这一行:

var row = new Row();

VS

var row = new SpecificRow();

因此,您可以将其提取到虚拟函数中,例如MakeRow(),然后ReadXml()可以不重复。

答案 1 :(得分:0)

Collection只能包含Row个对象时,为什么要进行投射?也许我错过了什么。

更新:

Collection[index] as Row;

会绕过异常,但它可能仍然不会导致你想要的东西(即:null而不是一行)

更新

将基础重构为:

    public void ReadXml<T>(XmlReader reader) where T : IRow
    {
        reader.ReadToFollowing(XmlNodeName);
        do
        {
            using (XmlReader rowReader = reader.ReadSubtree())
            {
                var row = default(T);
                row.ReadXml(rowReader);
                Collection.Add(row);
            }
        } while (reader.ReadToNextSibling(XmlNodeName));
    } 

为你工作?

答案 2 :(得分:0)

如果你想这样做,你可以......

public void ReadXml<T>(XmlReader reader) where T : IRow, new()
{
    reader.ReadToFollowing(XmlNodeName);
    do
    {
        using (XmlReader rowReader = reader.ReadSubtree())
        {
            var row = new T();
            row.ReadXml(rowReader);
            Collection.Add(row);
        }
    } while (reader.ReadToNextSibling(XmlNodeName));
} 

然后只要T是一个IRow类型并且它有一个默认构造函数,这个代码就可以了。 见Constraints on Type Parameters

另一种方法是使用模板方法

public void ReadXml(XmlReader reader) 
{
    reader.ReadToFollowing(XmlNodeName);
    do
    {
        using (XmlReader rowReader = reader.ReadSubtree())
        {
            Row row = CreateRow();
            row.ReadXml(rowReader);
            Collection.Add(row);
        }
    } while (reader.ReadToNextSibling(XmlNodeName));
} 

protected virtual Row CreateRow()
{
    return new Row();
}

然后在

// Acts as a collection of SpecificRow objects, which inherit from Row.  Uses the same
// Collection<Row> that Rows defines which is fine since SpecificRow : Row.
[Serializable]
[XmlRoot("MySpecificRowList")]
public class SpecificRows : Rows, IXmlSerializable
{
    protected override Row CreateRow()
    {
        return new SpecificRow();
    }   
}

如果您的this[]方法返回Row类型,则无需覆盖它。基类已经做到了。

如果你希望this[]返回一个SpecificRow,那么在这个派生类中直接转换为...是安全的...但是......为什么要消耗这个代码需要知道它正在处理什么类型的行?如果确实如此,那么我会尝试对Row对象应用“tell do not ask”,因此Row的使用者不知道它正在与哪种类型的Row进行通信,再次消除了对此方法的需求。