多维数组到1D - 使其与protobuf一起使用

时间:2017-05-01 18:41:48

标签: c# multidimensional-array protocol-buffers protobuf-net

Protobuf不支持多调光阵列,因此我决定使用this实现从2D制作一维阵列。 当我调用MultiLoop函数时,我在ToProtoArray方法中得到Cannot cast from source type to destination type。关于如何解决这个问题的任何想法?

public static ProtoArray<T> ToProtoArray<T>(this System.Array array)
{
    // Copy dimensions (to be used for reconstruction).
    var dims = new int[array.Rank];
    for (int i = 0; i < array.Rank; i++) dims[i] = array.GetLength(i);
    // Copy the underlying data.
    var data = new T[array.Length];
    var k = 0;
    array.MultiLoop(indices => data[k++] = (T)array.GetValue(indices));
    // ^^^^^^^^^^ cannot cast from source type to destination type
    return new ProtoArray<T> { Dimensions = dims, Data = data };
}

public static System.Array ToArray<T>(this ProtoArray<T> protoArray)
{
    // Initialize array dynamically.
    var result = System.Array.CreateInstance(typeof(T), protoArray.Dimensions);
    // Copy the underlying data.
    var k = 0;
    result.MultiLoop(indices => result.SetValue(protoArray.Data[k++], indices));

    return result;
}

public static void MultiLoop(this System.Array array, System.Action<int[]> action)
{
    array.RecursiveLoop(0, new int[array.Rank], action);
}

private static void RecursiveLoop(this System.Array array, int level, int[] indices, System.Action<int[]> action)
{
    if (level == array.Rank)
    {
        action(indices);
    }
    else
    {
        for (indices[level] = 0; indices[level] < array.GetLength(level); indices[level]++)
        {
            RecursiveLoop(array, level + 1, indices, action);
        }
    }
}

[ProtoContract]
public class ProtoArray<T>
{
    [ProtoMember(1)]
    public int[] Dimensions { get; set; }
    [ProtoMember(2)]
    public T[] Data { get; set; }
}

以下是我如何使用它来序列化2D数组:

[ProtoContract]
public class Tile
{
    [ProtoMember(1)]
    public int x;
    [ProtoMember(2)]
    public int y;
    // ...
}
Tile[,] map; // meanwhile I assign the data to the array
map1d = Extensions.ToProtoArray<Tile[,]>(map);
using (var file = File.Create(path))
{
    Serializer.Serialize(file, map1d);
}

1 个答案:

答案 0 :(得分:2)

我认为这就是你所需要的:

public class ProtoArray<T>
{
    public ProtoArray(T[] array)
    {
        this.Data=array;
        this.Dimensions=new int[array.Length];
    }
    public ProtoArray(T[,] array)
    {
        int n = array.GetLength(0);
        int m = array.GetLength(1);
        this.Data=new T[n*m];
        for(int i = 0; i<n; i++)
        {
            for(int j = 0; j<m; j++)
            {
                // Row Major
                Data[i*m+j]=array[i, j];
                // For Column Major use Data[i+j*n]=array[i, j];
            }
        }
        this.Dimensions=new[] { n, m };
    }
    public int[] Dimensions { get; set; }
    public T[] Data { get; set; }
    public T[] ToArray()
    {
        if(Dimensions.Length==1)
        {
            return Data.Clone() as T[];
        }
        else
        {
            throw new NotSupportedException();
        }
    }
    public T[,] ToArray2()
    {
        if(Dimensions.Length==2)
        {
            int n = Dimensions[0], m = Dimensions[1];
            T[,] array = new T[n, m];
            for(int i = 0; i<n; i++)
            {
                for(int j = 0; j<m; j++)
                {
                    array[i, j]=Data[i*m+j];
                }
            }
            return array;
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

public class Tile
{
    public int x;
    public int y;
    // ...
}
class Program
{
    static void Main(string[] args)
    {
        Tile[,] map = new Tile[16, 4];

        ProtoArray<Tile> array = new ProtoArray<Tile>(map);
        //serialize array
        //
        // de-serialize array
        Tile[,] serialized_map = array.ToArray2();
    }
}