C#为什么struct类型的class字段占用的空间比struct的大小还多?

问题描述

对于仅包含单个 byte 字段的结构,我希望该结构在内存中消耗 1 个字节。如果我创建一个该结构类型的数组,结果是一个准确的期望。将该结构用作类中的字段时,我也有同样的期望,但是,令我惊讶的是,每个字段都占用 IntPtr 的大小!我认为这只会由于将类填充到下一个字大小而发生,但无论如何似乎都是这种情况。这是为什么?

[编辑] 更新测量值

方法 平均 错误 StdDev Gen 0 Gen 1 第 2 代 已分配
AllocOneByte 7.453 ns 0.0885 ns 0.0827 ns 0.0076 - - 24 B
AllocTwoBytes 8.394 ns 0.0873 ns 0.0816 ns 0.0076 - - 24 B
AllocThreeBytes 8.398 ns 0.0559 ns 0.0523 ns 0.0076 - - 24 B
AllocOneByteWrapper 8.373 ns 0.0934 ns 0.0873 ns 0.0076 - - 24 B
AllocTwoByteWrappers 9.815 ns 0.1256 ns 0.1175 ns 0.0102 - - 32 B
AllocThreeByteWrappers 11.274 ns 0.1527 ns 0.1429 ns 0.0127 - - 40 B
AllocOneByteArray 9.286 ns 0.1609 ns 0.1505 ns 0.0102 - - 32 B
AllocTwoBytesArray 9.396 ns 0.1038 ns 0.0920 ns 0.0102 - - 32 B
AllocThreeBytesArray 8.904 ns 0.1183 ns 0.1107 ns 0.0102 - - 32 B

public class Benchmark
{
    public class OneByte
    {
        public byte num;
    }

    public class TwoBytes
    {
        public byte num;
        public byte num2;
    }

    public class ThreeBytes
    {
        public byte num;
        public byte num2;
        public byte num3;
    }


    public struct ByteWrapper
    {
        public byte num;
    }

    public class OneByteWrapper
    {
        public ByteWrapper num;
    }

    public class TwoByteWrappers
    {
        public ByteWrapper num;
        public ByteWrapper num2;
    }

    public class ThreeByteWrappers
    {
        public ByteWrapper num;
        public ByteWrapper num2;
        public ByteWrapper num3;
    }


    [Benchmark]
    public OneByte AllocOneByte()
    {
        return new OneByte();
    }

    [Benchmark]
    public TwoBytes AllocTwoBytes()
    {
        return new TwoBytes();
    }

    [Benchmark]
    public ThreeBytes AllocThreeBytes()
    {
        return new ThreeBytes();
    }

    [Benchmark]
    public OneByteWrapper AllocOneByteWrapper()
    {
        return new OneByteWrapper();
    }

    [Benchmark]
    public TwoByteWrappers AllocTwoByteWrappers()
    {
        return new TwoByteWrappers();
    }

    [Benchmark]
    public ThreeByteWrappers AllocThreeByteWrappers()
    {
        return new ThreeByteWrappers();
    }

    [Benchmark]
    public ByteWrapper[] AllocOneByteArray()
    {
        return new ByteWrapper[1];
    }

    [Benchmark]
    public ByteWrapper[] AllocTwoBytesArray()
    {
        return new ByteWrapper[2];
    }

    [Benchmark]
    public ByteWrapper[] AllocThreeBytesArray()
    {
        return new ByteWrapper[3];
    }
}

[Edit2] 这与 Why isn't sizeof for a struct equal to the sum of sizeof of each member? 不同。这是关于使用小结构作为类中的字段。

解决方法

我在评论中意识到您说您对填充非常了解,但总的来说:这只是:填充。要进行更细粒度的控制并避免这种情况,您可以使用 [FieldOffset(...)](如果您需要避免不同类型的字段之间的填充)和 [StructLayout(...)](控制单个结构的定义大小)。从根本上说,ByteWrapper 被默认填充到字长。您可以使用 [StructLayout] 说服它不要这样做。

FWIW:对我来说,ByteWrapper 以 1 个字节出现,而 ThreeByteWrappers 以 8 个字节出现 - ThreeBytes 也是如此。

,

这当然不理想,但我设法通过将小结构字段包装在另一个结构中来解决这个问题。不幸的是,这只能保证适用于已知的结构体大小(因此使用泛型的结果可能出乎意料)。

public class Benchmark
{
    public struct ByteWrapper
    {
        public byte num;
    }

    public class OneByteWrapper
    {
        public ByteWrapper num;
    }

    public class TwoByteWrappers
    {
        public struct SmallFields
        {
            public ByteWrapper num;
            public ByteWrapper num2;
        }

        public SmallFields smallFields;
    }

    public class ThreeByteWrappers
    {
        public struct SmallFields
        {
            public ByteWrapper num;
            public ByteWrapper num2;
            public ByteWrapper num3;
        }

        public SmallFields smallFields;
    }

    [Benchmark]
    public OneByteWrapper AllocOneByteWrapper()
    {
        return new OneByteWrapper();
    }

    [Benchmark]
    public TwoByteWrappers AllocTwoByteWrappers()
    {
        return new TwoByteWrappers();
    }

    [Benchmark]
    public ThreeByteWrappers AllocThreeByteWrappers()
    {
        return new ThreeByteWrappers();
    }
}
方法 运行时 平均 错误 StdDev 比例 Gen 0 Gen 1 第 2 代 已分配
AllocOneByteWrapper .NET 4.8 9.747 ns 0.0903 ns 0.0844 ns 1.00 0.0306 - - 24 B
AllocOneByteWrapper .NET Core 5.0 9.787 ns 0.1139 ns 0.0951 ns 1.00 0.0076 - - 24 B
AllocTwoByteWrappers .NET 4.8 9.975 ns 0.0655 ns 0.0580 ns 1.05 0.0306 - - 24 B
AllocTwoByteWrappers .NET Core 5.0 9.474 ns 0.1106 ns 0.0981 ns 1.00 0.0076 - - 24 B
AllocThreeByteWrappers .NET 4.8 9.483 ns 0.0535 ns 0.0474 ns 1.00 0.0306 - - 24 B
AllocThreeByteWrappers .NET Core 5.0 9.446 ns 0.1235 ns 0.1031 ns 1.00 0.0076 - - 24 B