简体   繁体   English

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

[英]C# copy array of structs to array of doubles

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


var data = new Demo[128];

FillWithMeaningfulValues(data);

double[] doubles;

Copy(data, out doubles); // ?

How do I copy the demo array into the doubles array without having to for(...) through each element? 如何将演示阵列复制到双精度数组中,而不必(...)通过每个元素? In C++, I would use memcpy, but in C# I did not find what I need in Marshal.Copy. 在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));
}

You'll do something like this. 你会做这样的事情。 Marshal.Copy do provides you what you need. 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();
}

Since the struct is blittable, the array of the struct is blittable. 由于结构是blittable,结构的数组是blittable。 Therefore you can pin the array of struct and copy into the double array with Marshal.Copy . 因此,您可以使用Marshal.Copy将struct数组和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();
    }
}

You might do well to benchmark this against the simpler for loop that you want to avoid. 您可以将此基准与您想要避免的更简单的for循环进行基准测试。 It is plausible that the for loop will perform perfectly adequately. for循环可以完美地运行,这似乎是合理的。

It's possible to write a generic method that can convert arrays of any compatible type (by "compatible" I mean "elements must be value types and the size of the elements must be compatible"). 可以编写一个可以转换任何兼容类型的数组的通用方法(“兼容”我的意思是“元素必须是值类型,元素的大小必须兼容”)。

You can use P/Invoke to call the Windows API CopyMemory() method. 您可以使用P / Invoke调用Windows API CopyMemory()方法。

However, bear in mind that there may not be any performance advantage to doing it this way; 但是,请记住,这样做可能没有任何性能优势; you should perform careful timings to be sure. 你应该仔细检查时间。

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

For your example, you could call this like so: 对于您的示例,您可以这样调用:

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]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM