C# DataContractSerializer不支持矩形数组

C# DataContractSerializer不支持矩形数组,c#,C#,我今天遇到了一个意外问题,试图序列化/反序列化包含bool[,]DataMember的DataContract。csc和运行时都不反对这个定义,但是反序列化的bool[,]DataMember中的值就是不正确。阅读之后,我的最初反应是将有问题的属性转换为锯齿数组。然而,我很快就不得不放弃这种方法,因为当以对角线或随机方式访问时,锯齿阵列的性能非常糟糕(这正是我的用例)。因此,我最终编写了上述msdn线程中建议的解决方案的策划版本(在导出/导入时将矩形转换为锯齿形,反之亦然,请参见下面的代码摘录)

我今天遇到了一个意外问题,试图序列化/反序列化包含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)
的索引,并且仍然会计算整个数组中的位置)。您是对的,这些检查需要手动完成。正在更新。此代码不起作用。实例化多维数组必须具有指定的大小。因此,新布尔[,];这是一个错误。