gpt4 book ai didi

c# - XmlSerializer 在实现 IList 时如何/为什么以不同方式对待类?

转载 作者:太空宇宙 更新时间:2023-11-03 21:19:49 30 4
gpt4 key购买 nike

XmlSerializer正在调用 IList<T>.Add()在我的课上,我不明白为什么。

我有一个自定义类(层次结构中的几个类之一),其中包含我使用 XmlSerializer 与 XML 相互转换的数据。 .在我以前的代码版本中,这些类没有实现任何接口(interface),XML 序列化和反序列化似乎都按预期工作。

我现在正在编写一些使用此类中包含的数据的其他代码,我认为如果我可以通过 IList<T> 访问数据会很有帮助。接口(interface),所以我修改了我的类来实现该接口(interface)。 (本例中的“T”是我的另一个自定义类。)这不涉及向该类添加任何新字段;我实现了所有 required methods and properties就已经存储的数据而言。

我希望这不会以任何方式影响序列化。但是,当将 XML 数据反序列化到我的类中时,现在有些东西正在调用新的 Add()。我作为 IList<T> 的一部分实现的方法接口(interface)(这是一个问题,因为这个特定列表 IsReadOnlyAdd() 抛出 NotSupportedException )。

即使我的类的 XML 节点只是 <myClass/> 也会发生这种情况没有任何 XML 属性或子元素; XmlSerializer显然仍在创建一个新的 myOtherClass (未在 XML 文档中的任何地方命名)并尝试 Add()它到 myClass .

我在这方面搜索信息时遇到问题,因为大多数问题都涉及 XmlSerializerIList<T>似乎涉及人们试图序列化/反序列化 IList<T> 类型的变量.那不是我的情况;我没有 IList<T> 类型的变量代码中的任何位置。如果我不实现 IList<T>,我的类序列化和反序列化就很好了界面。

谁能给我解释一下为什么XmlSerializer正在调用 IList<T>.Add()在我的课上,和/或如何让它停止?

理想情况下,建议应与最终在 Unity3d (.NET 2.0) 中运行的此代码兼容。

最佳答案

XmlSerializer 要求所有集合都有一个 Add()方法,如 documentation 中所述:

The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases. A class that implements ICollection (such as CollectionBase) in addition to IEnumerable must have a public Item indexed property (indexer in C#) that takes an integer, and it must have a public Count property of type integer. The parameter to the Add method must be the same type as is returned from the Item property, or one of that type's bases. For classes that implement ICollection, values to be serialized are retrieved from the indexed Item property, not by calling GetEnumerator.

此外,如果一个集合有自己的可设置属性,这些将不会被序列化。这也在 docs 中阐明。 :

The following items can be serialized using the XmLSerializer class:

  • Classes that implement ICollection or IEnumerable: Only collections are serialized, not public properties.

要了解这在实践中如何发挥作用,请考虑以下类(class):

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,我会得到:

<Vector2>
<X>1</X>
<Y>2</Y>
</Vector2>

现在假设我想要一个实现了 IList<double> 的新版本.我添加接口(interface)并实现它,为调整列表大小的所有方法抛出异常:

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
}
}

现在如果我序列化 XML,我得到:

<ArrayOfDouble>
<double>1</double>
<double>2</double>
</ArrayOfDouble>

如您所见,它现在序列化为 double 值的集合,具有可设置的属性 XY省略了。然后,反序列化时,Add()方法将被调用,而不是 X 的 set 方法和 Y ,并抛出异常。

如果我尝试执行 IReadOnlyList<double>而不是 IList<double> , XmlSerializer由于缺少 Add(),构造函数现在抛出异常方法。

示例 fiddle .

强行没办法XmlSerializer将集合视为一个简单的对象,而不是 implement IXmlSerializable do it manually ,这是相当繁重的。 ( 有一个 DataContractSerializer 的变通方法,即应用 [DataContract] 而不是 [CollectionDataContract] —— 但是 DataContractSerializer 直到 .Net 3.5 才被引入,所以它被淘汰了。)

而不是实现 IList<T> ,您可能只想引入一个扩展方法来遍历类中的值,如下所示:

    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;
}
}

关于c# - XmlSerializer 在实现 IList<T> 时如何/为什么以不同方式对待类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31552724/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com