C#10 的只读记录结构是否保证与显式实现具有相同的大小和字段对齐方式?

Wat*_*ter 8 c# structlayout c#-10.0

我做需要连续数据的事情。现在使用 C# 10,我们可以执行public readonly record struct.

我喜欢记录所具有的自动 ToString 功能,所以为我完成这个功能真是太好了。

因此,以下等价吗?

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public readonly struct MyVector
{
    public readonly float X;
    public readonly float Y;
    public readonly float Z;

    public MyVector(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}
Run Code Online (Sandbox Code Playgroud)

与简洁的 C# 10 版本相比

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public readonly record struct MyVectorRecord(float X, float Y, float Z)
{
}
Run Code Online (Sandbox Code Playgroud)

或者我这样做会不小心踩到任何地雷吗?我的意思是,是否有任何事情在幕后进行,使得record我上面写的内容不能达到我想要的连续包装效果?我不能让记录插入填充、间距或做任何奇怪的事情。

我没有使用带有记录结构的向量类,而是将其用于说明目的。您可以忽略“浮点相等比较”之类的内容,因为我只关心是否可以将其传递给需要 X/Y/Z 连续序列的库。

Pan*_*vos 13

record不是新类型,它是应用于引用类型和现在值类型的特定行为。该结构仍然是一个结构。您可以在Sharplab.io上对此进行测试,以查看编译器在每种情况下生成的代码。

不过,记录使用属性,而不是原始字段,因此您只能将结构与属性进行比较来记录结构。这就是重要的区别

这个结构:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public readonly struct MyVectorRecord2
{ 
    public float X {get;} 
    public float Y {get;} 
    public float Z {get;}
    
     public MyVectorRecord2(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}
Run Code Online (Sandbox Code Playgroud)

产生

[StructLayout(LayoutKind.Sequential, Pack = 4)]
[IsReadOnly]
public struct MyVectorRecord2
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly float <X>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly float <Y>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly float <Z>k__BackingField;

    public float X
    {
        [CompilerGenerated]
        get
        {
            return <X>k__BackingField;
        }
    }

    public float Y
    {
        [CompilerGenerated]
        get
        {
            return <Y>k__BackingField;
        }
    }

    public float Z
    {
        [CompilerGenerated]
        get
        {
            return <Z>k__BackingField;
        }
    }

    public MyVectorRecord2(float x, float y, float z)
    {
        <X>k__BackingField = x;
        <Y>k__BackingField = y;
        <Z>k__BackingField = z;
    }
}
Run Code Online (Sandbox Code Playgroud)

记录的同时

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public readonly record struct MyVectorRecord(float X, float Y, float Z)
{
}
Run Code Online (Sandbox Code Playgroud)

产生:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
[IsReadOnly]
public struct MyVectorRecord : IEquatable<MyVectorRecord>
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly float <X>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly float <Y>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly float <Z>k__BackingField;

    public float X
    {
        [CompilerGenerated]
        get
        {
            return <X>k__BackingField;
        }
        [CompilerGenerated]
        init
        {
            <X>k__BackingField = value;
        }
    }

    public float Y
    {
        [CompilerGenerated]
        get
        {
            return <Y>k__BackingField;
        }
        [CompilerGenerated]
        init
        {
            <Y>k__BackingField = value;
        }
    }

    public float Z
    {
        [CompilerGenerated]
        get
        {
            return <Z>k__BackingField;
        }
        [CompilerGenerated]
        init
        {
            <Z>k__BackingField = value;
        }
    }

    public MyVectorRecord(float X, float Y, float Z)
    {
        <X>k__BackingField = X;
        <Y>k__BackingField = Y;
        <Z>k__BackingField = Z;
    }

    public override string ToString()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append("MyVectorRecord");
        stringBuilder.Append(" { ");
        if (PrintMembers(stringBuilder))
        {
            stringBuilder.Append(' ');
        }
        stringBuilder.Append('}');
        return stringBuilder.ToString();
    }

    private bool PrintMembers(StringBuilder builder)
    {
        builder.Append("X = ");
        builder.Append(X.ToString());
        builder.Append(", Y = ");
        builder.Append(Y.ToString());
        builder.Append(", Z = ");
        builder.Append(Z.ToString());
        return true;
    }

    public static bool operator !=(MyVectorRecord left, MyVectorRecord right)
    {
        return !(left == right);
    }

    public static bool operator ==(MyVectorRecord left, MyVectorRecord right)
    {
        return left.Equals(right);
    }

    public override int GetHashCode()
    {
        return (EqualityComparer<float>.Default.GetHashCode(<X>k__BackingField) * -1521134295 + EqualityComparer<float>.Default.GetHashCode(<Y>k__BackingField)) * -1521134295 + EqualityComparer<float>.Default.GetHashCode(<Z>k__BackingField);
    }

    public override bool Equals(object obj)
    {
        return obj is MyVectorRecord && Equals((MyVectorRecord)obj);
    }

    public bool Equals(MyVectorRecord other)
    {
        return EqualityComparer<float>.Default.Equals(<X>k__BackingField, other.<X>k__BackingField) && EqualityComparer<float>.Default.Equals(<Y>k__BackingField, other.<Y>k__BackingField) && EqualityComparer<float>.Default.Equals(<Z>k__BackingField, other.<Z>k__BackingField);
    }

    public void Deconstruct(out float X, out float Y, out float Z)
    {
        X = this.X;
        Y = this.Y;
        Z = this.Z;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,这个

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public readonly struct MyVector
{
    public readonly float X;
    public readonly float Y;
    public readonly float Z;

    public MyVector(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}
Run Code Online (Sandbox Code Playgroud)

除属性外,保持不变IsReadOnly

[StructLayout(LayoutKind.Sequential, Pack = 4)]
[IsReadOnly]
public struct MyVector
{
    public readonly float X;

    public readonly float Y;

    public readonly float Z;

    public MyVector(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}
Run Code Online (Sandbox Code Playgroud)

最大的区别在于具有字段的结构和具有公共属性的结构之间。之后,record struct与具有属性的结构相比,a 仅包含额外的方法。