何时使用Structs(II)

时间:2014-04-23 07:43:56

标签: c# class struct

继续:When to use struct?

在阅读了微软关于结构定义的规则后,我感到困惑:

  1. 它的实例大小小于16个字节。:如何计算16个字节以下的大小?将结构中的每个变量一起添加?如果是,可能大多数变量不能与这种类型一起使用(例如:如果在结构中嵌套字符串或一些非常复杂的类型,则此大小将远大于16字节。)

  2. 这是不可改变的:这是什么意思?这意味着“分配给变量的内存一旦创建就无法更改”吗?这就是解释何时创建结构的原因,它很快会被破坏(短暂的时间)? / p>

  3. 非常感谢!

5 个答案:

答案 0 :(得分:0)

struct的大小只是struct本身,而不是它所指的那些生成在堆上的对象。

对于字符串,这是一个引用,它会添加32位(4字节)或64(8),具体取决于您运行的CPU技术。实际的字符串在堆上,与此规则的目的无关。

这条规则的目的是当你传递结构时,它们被复制,逐字节复制,当它们开始变大时,复制往往会增加更多的开销,而不是你从避免施加压力上获得的开销。垃圾收集器。

Stack Overflow上有很多关于如何获取结构大小(或者在这方面有意义的东西)的问题,我建议你做一个搜索。简而言之,不,它不像简单地总计每个单独字段的大小那么容易,因为可能存在填充和对齐会影响这一点。

不可变结构是在给定值后不会更改的结构。最简单(但绝不是唯一)这样做的方法是创建所有字段readonly,这意味着一旦通过构造函数给出了值,就不能(很容易)更改它们。

此规则的目的是存在与装箱,拆箱,复制等相关的隐藏陷阱,如果您创建可变结构,则会在工作中抛出扳手。

关于这个话题还有很多问题,搜索“可变结构是邪恶的”。


这是“邪恶的可变结构”的一个例子。鉴于此代码:

public struct Struct
{
    public int Value;
    public void Change()
    {
        Value++;
    }
}

private Struct _Struct = new Struct();
public Struct GetStruct()
{
    return _Struct;
}

public Struct StructProperty
{
    get
    {
        return _Struct;
    }
}

这将“工作”,它将先输出0然后输出1,结构已更改。

void Main()
{
    _Struct.Value.Dump();
    _Struct.Change();
    _Struct.Value.Dump();
}

这不会,它将输出0和0,因为从属性返回的副本已更改。

void Main()
{
    _Struct.Value.Dump();
    StructProperty.Change();
    _Struct.Value.Dump();
}

这也不会,同样的结果:

void Main()
{
    _Struct.Value.Dump();
    GetStruct().Change();
    _Struct.Value.Dump();
}

注意:如果你的预期目标是实际上没有打扰底层结构,那么从这个角度来看,这是“有效的”,但我对可变结构的所有经验告诉我,你很快就会过早'移动结构并隐式期望它们的行为类似于实例引用。

答案 1 :(得分:0)

  

1.实例大小小于16字节。:如何计算16字节以下的大小?在结构中添加每个变量吗?

简而言之,是的。由于这只是一个指导原则,因此不一定非准确。

  

如果是,可能大多数变量都不能与这种类型一起使用(例如:如果在结构中嵌入字符串或一些非常复杂的类型,则此大小将远大于16字节。)

String是一个班级。因此,类型string的变量指向存储器中的位置并占用32位或64位。是的,在结构中嵌入复杂的类型可能不是一个好主意。

  

2.它是不可改变的:这是什么意思?这意味着“分配给变量的内存一旦创建就无法更改”吗?这就是解释何时创建结构的原因,它很快就会被填充(短暂的时间)?

不,这与记忆无关。它只是意味着它的状态无法修改。

示例:这是一个可变的结构:

struct MyStruct
{
    private int someValue;

    public MyStruct(int initialValue) { someValue = initialValue; }

    public int SomeValue { get; set; } // someValue can be modified here through the setter
}

这是一个不可变的结构:

struct MyStruct
{
    private int someValue;

    public MyStruct(int initialValue) { someValue = initialValue; }

    public int SomeValue { get; } // No way to modify someValue
}

进一步阅读:Why are mutable structs “evil”?

答案 2 :(得分:0)

我个人建议远离C#中的结构,除非你绝对需要它们用于互操作或实际性能优势(可能很小)。

通常,结构体代表一个具体的数据结构,您永远不需要更改它。您不能像通常使用类一样引用结构。当您创建对结构的新引用时,您实际上是在制作整个结构的副本。你对这个新结构的任何机会都不会影响原始结构。

结构体如何工作的快速示例(与普通类相比)

var s = new SomeStruct();
s.SomeField = 5;

var y = s;
y.SomeField = 10;

Console.WriteLine(s.SomeField); //Prints 5
Console.WriteLine(y.SomeField); //Prints 10

答案 3 :(得分:0)

  

如何计算16字节以下的大小?在结构中添加每个变量吗?

一般来说,是的。值类型的大小通过对其字段大小求和来计算。如果结构有两个类型为int的字段,则它将具有4 + 4 = 8个字节。 如果您要计算引用类型的大小,则必须考虑其标头(引用TypeHandlesyncblk),这是32位上的8字节宽系统和64位系统上的16字节宽。

  

如果是,可能大多数变量都不能与这种类型一起使用(例如:如果在结构中嵌入字符串或一些非常复杂的类型,则此大小将远大于16字节。)

不,因为值类型将包含字符串的指针,而不是字符串本身。该指针在32位系统上为4个字节,在64位系统上为8个字节。传递值类型并复制它时,只会复制对该字符串的引用。

  

这是不可改变的:这是什么意思?

这意味着可观察数据无法改变。如果公开名为Age的字段/属性或名为GetAge()的方法,则必须始终在创建值类型后返回相同的值。另请参阅Why are mutable structs evil?

答案 4 :(得分:0)

结构不是一个对象 - 它是一堆与胶带粘在一起的变量。在某些情况下,一个语义上需要一个对象,但一个结构可能是一个适当的低成本替代品。在其他情况下,人们希望将一堆变量与胶带粘在一起。

可变结构的行为与对象的行为不同。因此,对于像行为一样的结构,它必须是不可变的。此外,与传递或复制参考变量的成本不同,参考变量与所引用的东西的大小无关,传递或复制结构的成本随着大小而增加。如果一个结构很大并且经过很多,那么额外的成本就会很高。如果一个结构没有经过多少,那么这个尺寸确实无关紧要。另一方面,用结构替换小类的性能提高远远大于用较大的结构替换可能实现的最大可能性。

如果想要的是一堆变量与胶带粘在一起(例如一个点的坐标),那么应该使用由一堆公共字段组成的结构。对这样一个结构的两秒钟的一瞥就会发现,除了字段的名称和磁带之外,它几乎与其他任何类似的结构一样,并且可能是EqualsHashCode的覆盖。和ToString()。虽然结构有时候是合理的,但实用程序"方法,通常一个好主意是修改结构的方法是静态的,并接受有问题的结构作为ref参数。这将有助于在视觉上强化该方法作用于变量聚合而不是对象的事实,并且还将避免编译器可能将结构的副本替换为" real"之一。

如果试图设计一个应该像对象一样的结构,MS准则就相当不错了。但是,如果想要将一堆变量与胶带粘在一起,就应该忽略它们 - 特别是"单个实体"部分。一个点的坐标具有独立数字意义的事实应被视为Point应该(并且是)结构的一部分,而不是反对它的论据。