我有一个数组Vector2f结构,每个结构包含两个浮点数,我想将它传递给一个采用浮点数组的函数.这些结构代表2d坐标,我想要最终结果[x0, y0, x1, y1, ... xn, yn].一些代码来演示:
using System;
using System.Runtime.InteropServices;
public class Test
{
[StructLayout(LayoutKind.Sequential)]
public struct Vector2f
{
float x;
float y;
public Vector2f(float x, float y)
{
this.x = x;
this.y = y;
}
}
public static void Main()
{
Vector2f[] structs = new Vector2f[]
{
new Vector2f(1f, 2f),
new Vector2f(3f, 4f)
};
// I want this to contain 1f, 2f, 3f, 4f
// But Syntax error, cannot convert type!
float[] floats = (float[])structs;
}
}
Run Code Online (Sandbox Code Playgroud)
通过将内容复制到新的浮点数组中,这很容易,但数据变大,不复制它会很好.
由于内存布局,这可能无法实现.
如果您实际上不需要传递真正的数组,而只是像数组一样可以访问的东西,您可以执行以下操作(未经测试):
public sealed class FloatArrayAdaptor : IReadOnlyList<float>
{
private Vector2f[] _data;
public FloatArrayAdaptor(Vector2f[] data)
{
_data = data;
}
public IEnumerator<float> GetEnumerator()
{
for (int i = 0; i < _data.Length; i++)
{
yield return _data[i].x;
yield return _data[i].y;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count
{
get { return 2*_data.Length; }
}
public float this[int index]
{
get
{
//TODO: Add appropriate range checking and whatnot
int i = index>>1;
bool isX = (index & 0x1) == 0;
return isX ? _data[i].x : _data[i].y;
}
}
}
Run Code Online (Sandbox Code Playgroud)
您将无法像在 C 中那样在 C# 中“重新转换”类型。最接近的方法是使用不安全代码并获取实际的 float*,但即便如此,您也不能将该指针视为用于传递给采用数组的方法的安全数组。
我做了一些实验,可以使用不安全的代码来“转换”类型,但这是一个非常非常糟糕的黑客行为,你实际上不应该这样做。尽管如此,它还是演示了有关 CLR 对象头数据的一些有趣的事情:
public static unsafe void Main()
{
Vector2f[] data = new Vector2f[10];
float[] dummy = new float[1];
//NOTE: This is horrible and you should never actually do it
//After this code, the original data array cannot be used safely anymore
fixed (void* rawData = &data[0])
fixed (void* rawDummy = &dummy[0])
{
int* intData = (int*)rawData;
int* intDummy = (int*)rawDummy;
//method table pointer is at X-4-sizeof(IntPtr)
//This is what identifies the type via RTTI
//We're going to forge our identity and change our size to change our type
//This assumes x86
intData[-2] = intDummy[-2];
//Our length is doubled
intData[-1] = 2*intData[-1];
}
if (data.GetType() == typeof(float[]))
Console.WriteLine("Type is now float[]!");
float[] floatData = (float[])(object)data;
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
基本上,我们替换了方法表指针,使类型现在看起来是 a float[],然后我们将数组的长度字段加倍来进行补偿。编译、运行并报告类型为 now float[]。也就是说,这很可能会在以后以某种惊人的方式破坏 GC,而且它肯定非常依赖于实现,而且这不涉及 x64 与 x86。不过,有趣的是……不过,这被称为“不安全”是有原因的。希望这有助于说明为什么不能以安全的方式支持它,因为 RTTI(通过方法表指针)被烘焙到存储数据的内存中。