2014-05-14 44 views
2
[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中找到需要的東西。C#將結構數組複製到雙精度數組

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)); 
} 
+0

您可以使用簡單的LINQ查詢達到的效果:'雙[] =雙打data.SelectMany(d =>新[ ] {dX,dY})。ToArray();'。 –

+1

@UlugbekUmirov儘管如此,它仍然會逐個遍歷它們。他試圖一次複製整個街區。 –

+0

@TimGoodman您說得對,但如果只有128個元素,我不會看到大量複製的性能優勢。 –

回答

3

由於結構體是blittable,結構體的數組是blittable。因此,您可以使用Marshal.Copy將結構數組複製到雙數組中。

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循環可以完美地執行,這似乎是合理的。

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(); 
} 
+0

是的。我已經使用這種技術在內存中混音。 +1 – spender

1

可以編寫一個通用的方法來轉換任何兼容類型的數組(通過「compatible」我的意思是「元素必須是值類型,元素的大小必須兼容」)。

您可以使用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]); 
相關問題