C# XmlSerializer在实现IList时如何/为什么对类进行不同的处理?
XmlSerializer正在我的类上调用IList.Add,我不明白为什么 我有一个自定义类,它是层次结构中几个类中的一个,包含使用XmlSerializer与XML进行转换的数据。在我的代码的前一个版本中,这些类没有实现任何接口,XML序列化和反序列化似乎都按预期工作 我现在正在编写使用此类中包含的数据的其他代码,我认为如果可以通过IList接口访问数据会很有帮助,因此我修改了类以实现该接口。本例中的T是我的另一个自定义类。这并不涉及向类中添加任何新字段;我根据已经存储的数据实现了所有这些功能 我希望这不会以任何方式影响序列化。但是,当将XML数据反序列化到我的类中时,有东西正在调用我作为IList接口的一部分实现的新Add方法,这是一个问题,因为这个特定列表是readOnly,因此Add抛出NotSupportedException 即使我的类的XML节点没有任何XML属性或子节点,也会发生这种情况;XmlSerializer显然仍在创建一个新的myOtherClass,它在XML文档中的任何地方都没有命名,并试图将其添加到myClass中 我在搜索这方面的信息时遇到了困难,因为大多数涉及XmlSerializer和IList的问题似乎都涉及试图序列化/反序列化IList类型变量的人。那不是我的情况;代码中没有IList类型的变量。如果我没有实现IList接口,我的类可以很好地序列化和反序列化 有人能解释为什么XmlSerializer在我的类上调用IList.Add,和/或如何停止它吗 建议最好与最终在Unity3d.NET 2.0中运行的代码兼容。如果不可靠地再现问题,就不可能提供任何具体的答案 除此之外,以下是一些可以帮助您的非特定注释: .NET序列化处理集合类型的方式不同于其他类型。默认情况下,实现任何IEnumerable接口(如IList)的类型被视为集合。这些类型通过枚举集合和存储单个元素来序列化。在反序列化时,.NET假定它可以使用Add方法填充反序列化对象。不幸的是,任何类型都会从Add方法抛出异常,或者更糟糕的是,根本没有实现异常。 在某些情况下,可以使用[DataContract]属性标记您的类型。这将覆盖默认行为,允许将您的类型视为非集合类型。 在其他情况下,您确实不应该首先实现IList,而是应该以不同的方式公开元素的枚举,例如作为返回枚举的属性。如果没有一个好的代码示例,那么任何代码示例都不可能说明这在您的场景中是否正确,但我认为至少有50/50的可能性。 如果不能可靠地再现问题,就不可能提供任何具体的答案 除此之外,以下是一些可以帮助您的非特定注释: .NET序列化处理集合类型的方式不同于其他类型。默认情况下,实现任何IEnumerable接口(如IList)的类型被视为集合。这些类型通过枚举集合和存储单个元素来序列化。在反序列化时,.NET假定它可以使用Add方法填充反序列化对象。不幸的是,任何类型都会从Add方法抛出异常,或者更糟糕的是,根本没有实现异常。 在某些情况下,可以使用[DataContract]属性标记您的类型。这将覆盖默认行为,允许将您的类型视为非集合类型。 在其他情况下,您确实不应该首先实现IList,而是应该以不同的方式公开元素的枚举,例如作为返回枚举的属性。如果没有一个好的代码示例,那么任何代码示例都不可能说明这在您的场景中是否正确,但我认为至少有50/50的可能性。 要求所有集合都具有Add方法,如中所述: XmlSerializer对实现IEnumerable或ICollection的类给予特殊处理。实现IEnumerable的类必须实现接受单个参数的公共Add方法。Add方法的参数的类型必须与从GetEnumerator返回的值的当前属性返回的类型相同,或者是该类型的基之一。一个实现ICollection的类,如CollectionBase和I Enumerable必须在C中具有接受整数的公共项索引属性索引器,并且必须具有integer类型的公共计数属性。Add方法的参数必须与从Item属性返回的参数类型相同,或者是该类型的基之一。对于实现ICollection的类,要序列化的值是从索引项属性中检索的,而不是通过调用GetEnumerator 此外,如果集合有自己的可设置属性,则不会序列化这些属性。这也在以下文件中详细说明: 可以使用XmLSerializer类序列化以下项: 实现ICollection或IEnumerable的类:只序列化集合,不序列化公共属性。 <> P>看看这在实践中是如何发挥的,请考虑下面的类:C# XmlSerializer在实现IList时如何/为什么对类进行不同的处理?,c#,xmlserializer,C#,Xmlserializer,XmlSerializer正在我的类上调用IList.Add,我不明白为什么 我有一个自定义类,它是层次结构中几个类中的一个,包含使用XmlSerializer与XML进行转换的数据。在我的代码的前一个版本中,这些类没有实现任何接口,XML序列化和反序列化似乎都按预期工作 我现在正在编写使用此类中包含的数据的其他代码,我认为如果可以通过IList接口访问数据会很有帮助,因此我修改了类以实现该接口。本例中的T是我的另一个自定义类。这并不涉及向类中添加任何新字段;我根据已经存储的数据实现了所有这些功
namespace V1
{
// https://stackoverflow.com/questions/31552724/how-why-does-xmlserializer-treat-a-class-differently-when-it-implements-ilistt
public class Vector2
{
public double X { get; set; }
public double Y { get; set; }
public Vector2() { }
public Vector2(double x, double y)
: this()
{
this.X = x;
this.Y = y;
}
public double this[int coord]
{
get
{
switch (coord)
{
case 0:
return X;
case 1:
return Y;
default:
throw new ArgumentOutOfRangeException();
}
}
set
{
switch (coord)
{
case 0:
X = value;
break;
case 1:
Y = value;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
如果我将其序列化为XML,我会得到:
要求所有集合都具有Add方法,如中所述:
XmlSerializer对实现IEnumerable或ICollection的类给予特殊处理。实现IEnumerable的类必须实现接受单个参数的公共Add方法。Add方法的参数的类型必须与从GetEnumerator返回的值的当前属性返回的类型相同,或者是该类型的基之一。除了IEnumerable之外,实现ICollection(如CollectionBase)的类必须在C中具有接受整数的公共项索引属性索引器,并且必须具有integer类型的公共计数属性。Add方法的参数必须与从Item属性返回的参数类型相同,或者是该类型的基之一。对于实现ICollection的类,要序列化的值是从索引项属性中检索的,而不是通过调用GetEnumerator
此外,如果集合有自己的可设置属性,则不会序列化这些属性。这也在以下文件中详细说明:
可以使用XmLSerializer类序列化以下项:
实现ICollection或IEnumerable的类:只序列化集合,不序列化公共属性。
<> P>看看这在实践中是如何发挥的,请考虑下面的类:
namespace V1
{
// https://stackoverflow.com/questions/31552724/how-why-does-xmlserializer-treat-a-class-differently-when-it-implements-ilistt
public class Vector2
{
public double X { get; set; }
public double Y { get; set; }
public Vector2() { }
public Vector2(double x, double y)
: this()
{
this.X = x;
this.Y = y;
}
public double this[int coord]
{
get
{
switch (coord)
{
case 0:
return X;
case 1:
return Y;
default:
throw new ArgumentOutOfRangeException();
}
}
set
{
switch (coord)
{
case 0:
X = value;
break;
case 1:
Y = value;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
如果我将其序列化为XML,我会得到:
您能否不只是实现IReadOnlyList而不是IList?实现接口并在某些方法中抛出异常有点代码味道。我不熟悉IReadOnlyList,所以感谢您指出这一点。然而,这段代码在理想情况下应该在Unity3d中工作,我相信这将我限制在.NET2.0中。您能不能不实现IList而实现IReadOnlyList?实现接口并在某些方法中抛出异常有点代码味道。我不熟悉IReadOnlyList,所以感谢您指出这一点。但是,理想情况下,这段代码应该在Unity3d内部工作,我认为这将我限制在.NET 2.0中。您是说.NET序列化假定实现IEnumerable的任何对象上都存在Add方法,即使Add不是IEnumerable接口的一部分?假定是错误的词。在序列化上下文中,Requires更为正确。显然,IEnumerable接口本身不需要Add实现。但是序列化假定在某个时刻将进行反序列化;反序列化集合需要某种机制来填充该集合。Microsoft选择使需求简单且独立于实际实现的接口:类型必须具有Add方法。有关更多详细信息,请参阅。我认为您混淆了XmlSerializer和DataContractSerializer。添加[DataContract]对XmlSerializer没有影响。@dbc:您是正确的,因此在某些情况下,例如在使用DataContractSerializer时以及在隐式使用它的场景中。我的回答旨在大致解决集合序列化的一般问题。XmlSerializer与DataContractSerializer有许多相同的行为,因此我已经包含了与这两者相关的要点。您是说,.NET序列化假定任何实现IEnumerable的对象上都存在Add方法,即使Add不是IEnumerable接口的一部分?假定是错误的词。在序列化上下文中,Requires更为正确。显然,IEnumerable接口本身不需要Add实现。但是序列化假定在某个时刻将进行反序列化;反序列化集合需要某种机制来填充该集合。Microsoft选择使需求简单且独立于实际实现的接口:类型必须具有Add方法。有关更多详细信息,请参阅。我认为您混淆了XmlSerializer和DataContractSerializer。添加[DataContract]对XmlSerializer没有影响。@dbc:您是正确的,因此在某些情况下,例如在使用DataContractSerializer时以及在隐式使用它的场景中。我的回答旨在大致解决集合序列化的一般问题。XmlSeri alizer与DataContractSerializer有许多相同的行为,因此我已经包含了与这两者相关的要点。
namespace V2
{
// https://stackoverflow.com/questions/31552724/how-why-does-xmlserializer-treat-a-class-differently-when-it-implements-ilistt
public class Vector2 : V1.Vector2, IList<double>
{
public Vector2() : base() { }
public Vector2(double x, double y) : base(x, y) { }
#region IList<double> Members
public int IndexOf(double item)
{
for (var i = 0; i < Count; i++)
if (this[i] == item)
return i;
return -1;
}
public void Insert(int index, double item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
#endregion
#region ICollection<double> Members
public void Add(double item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(double item)
{
return IndexOf(item) >= 0;
}
public void CopyTo(double[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count
{
get { return 2; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(double item)
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable<double> Members
public IEnumerator<double> GetEnumerator()
{
yield return X;
yield return Y;
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}
<ArrayOfDouble>
<double>1</double>
<double>2</double>
</ArrayOfDouble>
public static class Vector2Extensions
{
public static IEnumerable<double> Values(this Vector2 vec)
{
if (vec == null)
throw new ArgumentNullException();
yield return vec.X;
yield return vec.Y;
}
}