C#将结构数组复制到双精度数组

时间:2014-05-14 08:24:53

标签: c# memcpy value-type

[StructLayout(LayoutKind.Sequential)]
public struct Demo
{
    double X;
    double Y;
}


var data = new Demo[128];

FillWithMeaningfulValues(data);

double[] doubles;

Copy(data, out doubles); // ?

如何将演示阵列复制到双精度数组中而不必(?)通过每个元素?在C ++中,我会使用memcpy,但在C#中我没有找到Marshal.Copy中我需要的东西。

void MethodIDoNotWantToUse(Demo[] demo, out double[] doubles)
{
    doubles = new double[demo.Length * 2];
    for(int i = 0, j = 0; i < demo.Length; ++i)
    {
        doubles[j++] = demo[i].X;
        doubles[j++] = demo[i].Y;
    }  
}

void MethodIWouldPreferToUse(Demo[] demo, out double[] doubles)
{
    doubles = new double[demo.Length * 2];
    memcopy(doubles, demo, demo.Length * 2 * sizeof(double));
}

3 个答案:

答案 0 :(得分:5)

你会做这样的事情。 Marshal.Copy确实为您提供了所需的一切。

Demo[] array = new Demo[2];
array[0] = new Demo {X = 5.6, Y= 6.6};
array[1] = new Demo {X = 7.6, Y = 8.6};
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
    IntPtr pointer = handle.AddrOfPinnedObject();
    double[] copy = new double[array.Length*2];//This length may be calculated
    Marshal.Copy(pointer, copy, 0, copy.Length);
}
finally
{
    if (handle.IsAllocated)
        handle.Free();
}

答案 1 :(得分:4)

由于结构是blittable,结构的数组是blittable。因此,您可以使用Marshal.Copy将结构数组和副本固定到double数组中。

void CopyDemoArrayToDoubleArray(Demo[] demo, out double[] doubles)
{
    doubles = new double[demo.Length * 2];
    GCHandle gch = GCHandle.Alloc(demo, GCHandleType.Pinned);
    try
    {
        IntPtr demoPtr = gch.AddrOfPinnedObject();
        Marshal.Copy(demoPtr, doubles, 0, doubles.Length);
    }
    finally
    {
        gch.Free();
    }
}

您可以将此基准与您想要避免的更简单的for循环进行对比。 for循环可以完美地运行,这似乎是合理的。

答案 2 :(得分:1)

可以编写一个可以转换任何兼容类型数组的通用方法(通过&#34;兼容&#34;我的意思是&#34;元素必须是值类型,元素的大小必须兼容&#34;)。

您可以使用P / Invoke调用Windows API CopyMemory()方法。

但是,请记住,这样做可能没有任何性能优势;你应该仔细确定时间。

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

public TOut[] ConvertArray<TIn, TOut>(TIn[] input) where TIn:struct where TOut:struct
{
    if (input == null)
        throw new ArgumentNullException("input");

    int sizeTIn   = Marshal.SizeOf(typeof(TIn));
    int sizeTOut  = Marshal.SizeOf(typeof(TOut));
    int sizeBytes = input.Length*sizeTIn;

    if ((sizeBytes % sizeTOut) != 0)
        throw new ArgumentException("Size of input type is not compatible with size of output type.");

    int sizeOut = sizeBytes/sizeTOut;

    var output = new TOut[sizeOut];

    GCHandle inHandle  = GCHandle.Alloc(input,  GCHandleType.Pinned);
    GCHandle outHandle = GCHandle.Alloc(output, GCHandleType.Pinned);

    try
    {
        IntPtr inPtr  = inHandle.AddrOfPinnedObject();
        IntPtr outPtr = outHandle.AddrOfPinnedObject();

        CopyMemory(outPtr, inPtr, (uint)sizeBytes);
    }

    finally
    {
        outHandle.Free();
        inHandle.Free();
    }

    return output;
}

对于您的示例,您可以这样调用:

Demo[] test = new Demo[10];

for (int i = 0; i < 10; ++i)
    test[i] = new Demo {X = i, Y = i};

var result = ConvertArray<Demo, double>(test);

for (int i = 0; i < 20; ++i)
    Console.WriteLine(result[i]);