我有一个基本集合类和一个子集合类,每个类都是可序列化的。在测试中,我发现只是让孩子班的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.ReadXml
和SpecificRows.ReadXml
本质上是相同的代码。 XmlNodeName
和new Row()
/ new SpecificRow()
的值是差异。您如何建议我提取ReadXml
两个版本的所有常用功能?为一个方法创建一些泛型类是否愚蠢?对于冗长的代码示例感到抱歉,我只是想提供一个原因,我不能简单地在base.ReadXml
内拨打SpecificRows
。
答案 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进行通信,再次消除了对此方法的需求。