只为IntPtr创建一种任何类型的数组方法

时间:2013-12-16 21:00:27

标签: c#

有一种方法可以将double[] d转换为IntPtr,如:

public static IntPtr DoubleArrayToIntPtr(double[] d)
{
   IntPtr p = Marshal.AllocCoTaskMem(sizeof(double) * d.Length);
   Marshal.Copy(d, 0, p, d.Length);
   return p;
}

制作int[]float[]等的最佳方式是什么?我正在考虑为每种类型制作一个方法,例如添加int []:

public static IntPtr IntArrayToIntPtr(int[] d)
{
   IntPtr p = Marshal.AllocCoTaskMem(sizeof(int) * d.Length);
   Marshal.Copy(d, 0, p, d.Length);
   return p;
}

1。这种方法可以推广,如果是这样的话?

2。是否可以只在一行代码中获取指针(因为Marshal是无效方法)?

2 个答案:

答案 0 :(得分:4)

最好的方法是为每种类型创建一个为您完成此操作的扩展类。您不能将其设为通用,因为Marshal.Copy()不支持泛型类型。因此,您必须为每种类型bytecharintlongfloatdouble等复制您的方法。 ,您可以制作通用帮助方法,以确定您的尺寸为Marshsal

以下内容可能对您有用:

public static class MarshalExtender
{
    public static IntPtr CopyToCoTaskMem(this byte[] array)
    {
        var ptr = AllocArrayCoTaskMem(array);

        Marshal.Copy(array, 0, ptr, array.Length);
        return ptr;
    }

    // Copy the above method and replace types as needed (int, double, etc).

    // Helper method for allocating bytes with generic arrays.
    static IntPtr AllocArrayCoTaskMem<T>(T[] array)
    {
        var type = typeof(T);
        var size = Marshal.SizeOf(type) * array.Length;
        return Marshal.AllocCoTaskMem(size);
    }
}

主要的好处是在您的代码中,您可以轻松地执行以下操作:

var myArray = { 0, 1, 2, 3, 4, 5, ... }
var ptr = myArray.CopyToCoTaskMem();

注意:完成后请务必致电Marshal.FreeCoTaskMem()!我建议您的扩展方法返回IDisposable对象,以便将其包装在using块中。

例如:

public sealed class CoTaskMemoryHandle : IDisposable
{
    bool isDisposed;
    readonly IntPtr handle;

    public IntPtr Handle { get { return handle; } }

    public CoTaskMemoryHandle(IntPtr handle)
    {
        this.handle = handle;
    }

    public void Dispose()
    {
        OnDispose(true);
        GC.SuppressFinalize(this);
    }

    void OnDispose(bool isDisposing)
    {
        if (isDisposed) return;

        if (isDisposing)
        {
            if (handle != IntPtr.Zero)
                Marshal.FreeCoTaskMem(handle);
        }

        isDisposed = true;
    }
}

然后修改后的扩展类方法:

public static CoTaskMemoryHandle CopyToCoTaskMem(this byte[] array)
{
    var ptr = AllocArrayCoTaskMem(array);

    Marshal.Copy(array, 0, ptr, array.Length);
    return new CoTaskMemoryHandle(ptr);
}

这样您就可以安全地封装Alloc / Free平衡:

using(myArray.CopyToCoTaskMem())
{
   // Do something here..
}

答案 1 :(得分:2)

要回答你的第一个问题,是的,它可以推广:

public static IntPtr ArrayToIntPtr<T>(T[] d) where T : struct
{
     var arrayType = typeof(T);
     IntPtr p = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T)) * d.Length);
     if (arrayType == typeof(int))
     {
          Marshal.Copy((int[])(object)d, 0, p, d.Length);
     }
     //else if ....other types you want to handle
     return p;
}

我添加了struct的类型约束,它限制了值类型(short,double,int,structs)。你显然需要注意你传递的内容(比如你传入一个结构)。

为了解决你的第二个问题,不,我认为你不能把它减少到一行,即使你可以,你可能也不希望为了可读性。

修改
我已经修复了SiLo在评论中发现的问题,但是这段代码现在非常难看,我不能说我会推荐这种方法。它显然涉及很多类型检查和拳击,以获得正确的类型。事实上,我个人建议跟随Silo的answer而不是我的,虽然我至少已经表明你可以以一种准通用的方式做到这一点,尽管它首先打破了仿制药的精神。