C# 获取指向自身内部结构的指针(不安全上下文)

C# 获取指向自身内部结构的指针(不安全上下文),c#,pointers,struct,unsafe,C#,Pointers,Struct,Unsafe,长话短说,我将提供一个可能有用的简单示例: 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[i

长话短说,我将提供一个可能有用的简单示例:

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:

对于那些仍在提问的人:)

编辑2:

fixed
是否会在此处产生冗余开销? 或者这个结构已经修复,因此
fixed
在这里没有效果,只需要满足编译器的需要

你的意思是这样的


我承认它只解决了这一特定情况,而不是“一般”情况,但您可以通过这样做来避免
不安全
(特别是当您陈述您正在展示一个简化的示例时-但它可能对访问此问题的其他人有用):


首先,我不会为此使用不安全的代码,除非我首先确定(1)带有开关的明显代码是整个程序中最慢的代码,并且会导致明显的、用户可观察到的速度减慢,(2)使用不安全的代码修复性能问题

第二,如果我使用不安全的代码,对结构打包进行假设是非常危险的。CLR可以选择如何打包结构如果要执行此危险操作,则应使用struct layout属性确保浮点数正好位于需要的位置。

第三,是什么阻止有缺陷的调用者传递负索引或太大的索引

第四:

修复会在这里产生冗余开销吗

我不知道“冗余开销”是什么意思。“修复”使抖动告诉垃圾收集器“不要移动这个东西,因为我需要对它执行指针运算”。您正在进行短期修复,这是理想的;如果修复时间过长,则更可能因为无法移动固定的存储而导致集合混乱

第五:

或者这个结构已经修复,因此修复在这里没有效果,只需要满足编译器的需要


也许吧!“this”所指的变量可能已经是固定变量可能不是。编译器如何知道结构的“this”是否是对固定存储的引用?我们必须假设最坏的情况,所以你必须修复它。

我同意Eric的观点,你可能不想这样做,我怀疑Rob的解决方案也一样(同时避免使用
修复的
)。不过,值得注意的是,如果使用
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;
        }
    }
}
[StructLayout(LayoutKind.Explicit)]
公共结构向量3f
{
[字段偏移量(0)]
私人浮动x;
[FieldOffset(sizeof(float))]
私人游船;
[FieldOffset(2*sizeof(float))]
私有浮动z;
[字段偏移量(0)]
私有不安全固定浮动索引[3];
公共向量3F(浮点x、浮点y、浮点z)
{
这个.x=x;
这个。y=y;
这个。z=z;
}
公共浮点X{get{return X;}set{X=value;}}
公共浮点Y{get{return Y;}set{Y=value;}}
公共浮点Z{get{return Z;}set{Z=value;}}
公共不安全浮动此[int索引]
{
得到
{
如果(索引<0 | |索引>=3)
抛出新的IndexOutOfRangeException();
固定(浮动*b=索引)
返回b[索引];
}
设置
{
如果(索引<0 | |索引>=3)
抛出新的IndexOutOfRangeException();
固定(浮动*b=索引)
b[指数]=数值;
}
}
}

您想让v[0]返回x字段值、v[1]y值和v[2]-z?尝试:获取第一个字段的地址。老实说:我从未修复过此问题。这可能是完全错误的。但它真的比另一种方法更快吗?我确实写了一些类似的代码,在这种情况下,不安全的固定代码并不比安全代码快。@CodeInChaos不,我肯定不是,但这应该是一个“你如何质疑它”而不是一个“什么会更好”的答案。好吧,ordang的方法工作得很好。你的建议实际上是我想要避免的,也是我强烈反对的,因为它很慢而且是陈词滥调;)但是问题中的
struct
在托管内存中有一个顺序布局,默认情况下是连续打包的类型,不是吗?@ordag:我不知道,是吗?C#规范规定“未指定将成员打包到结构中的顺序。出于对齐目的,在结构的开头、内部和结尾可能有未命名的填充。”您无法保证以任何特定方式打包结构。如果您碰巧正在使用的某个实现恰好以您喜欢的方式打包了结构,那么这就是您的好运。你想相信这种运气到什么程度?@Haroogan:我不知道当你在机器上运行代码时,垃圾收集器会发生什么。你是唯一可能知道的人。如果您想知道在计算机上运行代码时是否存在性能问题,请在计算机上运行代码,看看它是否太慢。@ordag:运行时的某些版本或C#编译器的某些实现可能会向您提供这种保证。我的观点是,保证不是由C语言做出的。Mono是否提供了相同的担保?通过契约框架?通过一些未来的实施?我不愿意做出这些假设,但我的观点受到多年调试可怕错误的影响,这些错误是由人们造成的——其中一些人是我的过去版本——他们对结构布局的实现细节做出了不必要的假设。结果证明违反了假设。@Haroogan您担心的是与
修复相关的成本,但它是
get
{
    // (index validation omitted)
    fixed (Vector3f* thisPtr = &this)
    {
        return ((float*)thisPtr)[index];
    }
}
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();
            }
        }
    }
}
[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;
        }
    }
}