C#:将泛型指针转换为数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/985646/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C#: convert generic pointer to array
提问by wj32
I want to convert a byte*
to a byte[]
, but I also want to have a reusable function to do this:
我想将 a 转换byte*
为 a byte[]
,但我也想有一个可重用的函数来做到这一点:
public unsafe static T[] Create<T>(T* ptr, int length)
{
T[] array = new T[length];
for (int i = 0; i < length; i++)
array[i] = ptr[i];
return array;
}
Unfortunately I get a compiler error because T might be a ".NET managed type" and we can't have pointers to those. Even more frustrating is that there is no generic type constraint which can restrict T to "unmanaged types". Is there a built-in .NET function to do this? Any ideas?
不幸的是,我收到一个编译器错误,因为 T 可能是“.NET 托管类型”并且我们无法指向那些. 更令人沮丧的是,没有可以将 T 限制为“非托管类型”的泛型类型约束。是否有内置的 .NET 函数来执行此操作?有任何想法吗?
采纳答案by Jerome Laban
The method that could match what you are trying to do is Marshal.Copy, but it does not take the appropriate parameters to make a generic method.
可以匹配您尝试执行的操作的方法是Marshal.Copy,但它不需要适当的参数来创建通用方法。
Although there it is not possible to write a generic method with generic constraints that could describe what is possible, not every type can be allowed to be copied using an "unsafe" way. There are some exceptions; classes are one of these.
虽然不可能用泛型约束来描述什么是可能的,但并不是每种类型都可以使用“不安全”的方式进行复制。有一些例外;类就是其中之一。
Here is a sample code:
这是一个示例代码:
public unsafe static T[] Create<T>(void* source, int length)
{
var type = typeof(T);
var sizeInBytes = Marshal.SizeOf(typeof(T));
T[] output = new T[length];
if (type.IsPrimitive)
{
// Make sure the array won't be moved around by the GC
var handle = GCHandle.Alloc(output, GCHandleType.Pinned);
var destination = (byte*)handle.AddrOfPinnedObject().ToPointer();
var byteLength = length * sizeInBytes;
// There are faster ways to do this, particularly by using wider types or by
// handling special lengths.
for (int i = 0; i < byteLength; i++)
destination[i] = ((byte*)source)[i];
handle.Free();
}
else if (type.IsValueType)
{
if (!type.IsLayoutSequential && !type.IsExplicitLayout)
{
throw new InvalidOperationException(string.Format("{0} does not define a StructLayout attribute", type));
}
IntPtr sourcePtr = new IntPtr(source);
for (int i = 0; i < length; i++)
{
IntPtr p = new IntPtr((byte*)source + i * sizeInBytes);
output[i] = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T));
}
}
else
{
throw new InvalidOperationException(string.Format("{0} is not supported", type));
}
return output;
}
unsafe static void Main(string[] args)
{
var arrayDouble = Enumerable.Range(1, 1024)
.Select(i => (double)i)
.ToArray();
fixed (double* p = arrayDouble)
{
var array2 = Create<double>(p, arrayDouble.Length);
Assert.AreEqual(arrayDouble, array2);
}
var arrayPoint = Enumerable.Range(1, 1024)
.Select(i => new Point(i, i * 2 + 1))
.ToArray();
fixed (Point* p = arrayPoint)
{
var array2 = Create<Point>(p, arrayPoint.Length);
Assert.AreEqual(arrayPoint, array2);
}
}
The method can be generic, but it cannot take a pointer of a generic type. This is not an issue since pointers covariance is helping, but this has the unfortunate effect of preventing an implicit resolution of the generic argument type. You then have to specify MakeArray explicitly.
该方法可以是泛型的,但不能采用泛型类型的指针。这不是问题,因为指针协方差是有帮助的,但这具有阻止泛型参数类型的隐式解析的不幸效果。然后,您必须明确指定 MakeArray。
I've added a special case for the structures, where it is best to have types that specify a struct layout. This might not be an issue in your case, but if the pointer data is coming from native C or C++ code, specifying a layout kind is important (The CLR might choose to reorder fields to have a better memory alignment).
我为结构添加了一个特殊情况,最好使用指定struct layout 的类型。在您的情况下,这可能不是问题,但如果指针数据来自本机 C 或 C++ 代码,则指定布局类型很重要(CLR 可能会选择对字段重新排序以获得更好的内存对齐)。
But if the pointer is coming exclusively from data generated by managed code, then you can remove the check.
但是如果指针完全来自托管代码生成的数据,那么您可以删除检查。
Also, if the performance is an issue, there are better algorithms to copy the data than doing it byte by byte. (See the countless implementations of memcpy for reference)
此外,如果性能是一个问题,有比逐字节复制数据更好的算法。(参考 memcpy 的无数实现)
回答by samjudson
I have no idea whatsoever if the following would work, but it might (at least it compiles :):
我不知道以下是否可行,但它可能(至少它可以编译:):
public unsafe static T[] Create<T>(void* ptr, int length) where T : struct
{
T[] array = new T[length];
for (int i = 0; i < length; i++)
{
array[i] = (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T));
}
return array;
}
The key is to use Marshal.PtrToStructure
to convert to the correct type.
关键是要用Marshal.PtrToStructure
到转换成正确的类型。
回答by Henk Holterman
Seems that the question becomes: How to specify a generic Type to be a simple type.
似乎问题变成了:如何将泛型类型指定为简单类型。
unsafe void Foo<T>() : where T : struct
{
T* p;
}
Gives the error:
Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')
给出错误:
无法获取其地址、获取其大小或声明指向托管类型 ('T') 的指针
回答by twon33
How about this?
这个怎么样?
static unsafe T[] MakeArray<T>(void* t, int length, int tSizeInBytes) where T:struct
{
T[] result = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr p = new IntPtr((byte*)t + (i * tSizeInBytes));
result[i] = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T));
}
return result;
}
We can't use sizeof(T) here, but the caller can do something like
我们不能在这里使用 sizeof(T),但调用者可以做类似的事情
byte[] b = MakeArray<byte>(pBytes, lenBytes, sizeof(byte));