题
我想转换一个 byte*
到 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;
}
不幸的是,我遇到了编译器错误,因为T可能是“ .NET托管类型”,我们不能有指示 那些. 。更令人沮丧的是,没有通用类型的约束可以将t限制为“不受管理类型”。是否有内置的.NET函数可以执行此操作?有任何想法吗?
解决方案
可以匹配您要做的事情的方法是 元帅, ,但不需要适当的参数来制作通用方法。
尽管不可能编写具有通用约束的通用方法,该方法可以描述可能的方法,但并非可以使用“不安全”方式复制每种类型。有一些例外;课程就是其中之一。
这是一个示例代码:
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);
}
}
该方法可以是通用的,但不能采用通用类型的指针。这不是一个问题,因为Pointers协方差有所帮助,但这具有不幸的效果,即防止对通用参数类型的隐性解决。然后,您必须明确指定makearray。
我已经为结构添加了一个特殊情况,最好拥有指定一个类型的类型 结构布局. 。在您的情况下,这可能不是问题,但是如果指针数据来自本机C或C ++代码,则指定布局类型很重要(CLR可能会选择重新排序字段以具有更好的内存对齐)。
但是,如果指针仅来自由托管代码生成的数据,则可以删除支票。
另外,如果性能是一个问题,则比通过字节来复制数据更好,可以复制数据。 (参考,请参见无数Memcpy的实现)
其他提示
似乎问题变成了:如何将通用类型指定为简单类型。
unsafe void Foo<T>() : where T : struct
{
T* p;
}
给出错误:
无法获取地址,获取大小或声明指向托管类型的指针('t')
这个怎么样?
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;
}
我们不能在这里使用sizeof(t),但是呼叫者可以做类似的事情
byte[] b = MakeArray<byte>(pBytes, lenBytes, sizeof(byte));
我不知道以下内容是否有效,但是它可能(至少它编译为:):
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;
}
关键是使用 Marshal.PtrToStructure
转换为正确的类型。
不隶属于 StackOverflow