长话短说,我将提供一个可能有用的简单示例:
public struct Vector3f {
public float x;
public float y;
public float z;
public unsafe float this[int index] {
get {
// Get "p" somehow, so that it points to "this"...
return p[index];
}
set {
// Get "p" somehow, so that it points to "this"...
p[index] = value;
}
}
}
我想你明白了我的观点:
var v = new Vector3f();
Assert(v.x == v[0]);
编辑1:
对于那些仍然会问的人:)
Assert(v.y == v[1]);
Assert(v.z == v[2]);
编辑2:
这里fixed
会产生多余的开销吗?
或者这个结构可能已经修复,因此fixed
在这里没有效果,只需要满足编译器的需要? Possible answer
答案 0 :(得分:8)
首先,我不会使用不安全的代码,除非我首先确定(1)带有开关的明显代码整个程序中最慢的代码并导致重要的用户 - 可观察的减速,以及(2)转向不安全的代码修复了性能问题。
其次,如果我使用不安全的代码,那么对结构打包做出假设是非常危险的。 CLR在如何选择打包结构方面具有广泛的优势。 如果你打算做这个危险的事情,那么你应该使用struct layout属性来确保浮动正好在你需要它的地方。
第三,是什么阻止了一个错误的调用者传递否定索引或过大的索引?
第四:
修复是否会在此处创建冗余开销?
我不知道“冗余开销”意味着什么。 “fixed”使得抖动告诉垃圾收集器“不要移动这个东西,因为我需要对它进行指针运算”。你正在修理一段时间,这是理想的;长时间固定会使收藏品更加混乱,因为收起的存储空间无法移动。
第五:
或许这个结构已经修复,因此修复在这里没有效果,只需要满足编译器的需要?
也许吧!也许“this”引用的变量已经是固定变量。 可能不是。 编译器如何知道结构的“this”是否是对固定存储的引用?我们必须假设最坏的情况,所以你需要修复它。
答案 1 :(得分:5)
你的意思是这样的吗?
get
{
// (index validation omitted)
fixed (Vector3f* thisPtr = &this)
{
return ((float*)thisPtr)[index];
}
}
答案 2 :(得分:1)
我承认它只解决了这个具体案例,而不是“一般”案例,但你可以通过做这样的事情来避免unsafe
(特别是当你说你正在展示一个简化的例子 - 但它可能对访问此问题的其他人有用):
public struct Vector3f
{
public float x;
public float y;
public float z;
public float this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
default:
throw new ArgumentOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
答案 3 :(得分:0)
我同意Eric你可能不想这样做,我怀疑Rob的解决方案同样如此(同时避免使用fixed
)。但是,值得注意的是,如果使用LayoutKind.Explicit
:
[StructLayout(LayoutKind.Explicit)]
public struct Vector3f
{
[FieldOffset(0)]
private float x;
[FieldOffset(sizeof(float))]
private float y;
[FieldOffset(2 * sizeof(float))]
private float z;
[FieldOffset(0)]
private unsafe fixed float indexed[3];
public Vector3f(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Z { get { return z; } set { z = value; } }
public unsafe float this[int index]
{
get
{
if (index < 0 || index >= 3)
throw new IndexOutOfRangeException();
fixed (float* b = indexed)
return b[index];
}
set
{
if (index < 0 || index >= 3)
throw new IndexOutOfRangeException();
fixed (float* b = indexed)
b[index] = value;
}
}
}