C# DataContractSerializer不支持矩形数组
我今天遇到了一个意外问题,试图序列化/反序列化包含bool[,]DataMember的DataContract。csc和运行时都不反对这个定义,但是反序列化的bool[,]DataMember中的值就是不正确。阅读之后,我的最初反应是将有问题的属性转换为锯齿数组。然而,我很快就不得不放弃这种方法,因为当以对角线或随机方式访问时,锯齿阵列的性能非常糟糕(这正是我的用例)。因此,我最终编写了上述msdn线程中建议的解决方案的策划版本(在导出/导入时将矩形转换为锯齿形,反之亦然,请参见下面的代码摘录),效果很好C# DataContractSerializer不支持矩形数组,c#,C#,我今天遇到了一个意外问题,试图序列化/反序列化包含bool[,]DataMember的DataContract。csc和运行时都不反对这个定义,但是反序列化的bool[,]DataMember中的值就是不正确。阅读之后,我的最初反应是将有问题的属性转换为锯齿数组。然而,我很快就不得不放弃这种方法,因为当以对角线或随机方式访问时,锯齿阵列的性能非常糟糕(这正是我的用例)。因此,我最终编写了上述msdn线程中建议的解决方案的策划版本(在导出/导入时将矩形转换为锯齿形,反之亦然,请参见下面的代码摘录)
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is GridArrayWrapper)
{
bool[,] arr;
GridArrayWrapper wrapper = (GridArrayWrapper)obj;
if (wrapper.Array == null) return null;
int d0 = wrapper.Array.Length;
if (d0 == 0)
{
return new bool[0, 0];
}
var d1 = wrapper.Array[0].Length;
arr = new bool[d0, d1];
for (int i = 0; i < d0; i++)
{
if (wrapper.Array[i].Length != d1) throw new ArgumentException("Not a rectangular array");
for (var j = 0; j < d1; j++)
{
arr[i, j] = wrapper.Array[i][j];
}
}
return arr;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is bool[,])
{
bool[,] arr = (bool[,])obj;
GridArrayWrapper wrapper = new GridArrayWrapper();
int d0 = arr.GetLength(0);
int d1 = arr.GetLength(1);
wrapper.Array = new bool[d0][];
for (int i = 0; i < wrapper.Array.Length; i++)
{
wrapper.Array[i] = new bool[d1];
for (int j = 0; j < d1; j++)
{
wrapper.Array[i][j] = arr[i, j];
}
}
return wrapper;
}
return obj;
}
公共对象GetDeserializedObject(对象对象,类型targetType)
{
if(obj是GridArrayRapper)
{
bool[,]arr;
GridArrayWrapper包装器=(GridArrayWrapper)obj;
if(wrapper.Array==null)返回null;
int d0=wrapper.Array.Length;
如果(d0==0)
{
返回新bool[0,0];
}
var d1=wrapper.Array[0]。长度;
arr=新布尔值[d0,d1];
对于(int i=0;i
但是,我想知道是否有更简洁的解决方案来解决这个或另一个方法。我不是实现GetDeserializedObject和GetObjectToSerialize,而是公开一个不同的对象进行序列化。顺便说一下,一个一维数组就足够了。 我会这样做:
//No Datamember here
public bool[,] Data;
[DataMember(Name="Data")]
public bool[] XmlData
{
get {
bool[] tmp = new bool[Data.GetLength(0) * Data.GetLength(1)];
Buffer.BlockCopy(Data, 0, tmp, 0, tmp.Length * sizeof(bool));
return tmp;
}
set {
bool[,] tmp = new bool[,];
Buffer.BlockCopy(value, 0, tmp, 0, value.Length * sizeof(bool));
this.Data = tmp;
}
}
首先需要多维数组的原因是什么 如果使用一维数组,只需通过非常简单的计算来计算适当的索引:
array[x, y]
变成
array[(y * width) + x]
编辑:正如评论中提到的,您将丢失一些有用的边界检查。如果这是一个问题,您可以重新添加它们:
if (x < 0 || x > width || y < 0 || y > height)
throw new IndexOutOfRangeException();
if(x<0 | | x>宽度| | y<0 | | y>高度)
抛出新的IndexOutOfRangeException();
注意:如果x是有效的,那么y个事例就已经被数组抛出了。“锯齿状数组的性能非常糟糕”-但是与I/O相比,这真的很重要吗,转换和序列化?持久性格式和运行时格式不必是相同的。应用程序的核心—所讨论的阵列被访问了数百万次(而且它是一款CPU功能不太强大的手机应用程序)。序列化仅在应用程序停用或关闭时发生(不经常)。是的,但您会丢失边界检查的一些元素(对于
3x4
数组,您可以指定(6,1)
的索引,并且仍然会计算整个数组中的位置)。您是对的,这些检查需要手动完成。正在更新。此代码不起作用。实例化多维数组必须具有指定的大小。因此,新布尔[,];这是一个错误。