代码之家  ›  专栏  ›  技术社区  ›  Timothy Khouri

结构是否比类“更快”—一般情况下还是在.NET框架中?

  •  16
  • Timothy Khouri  · 技术社区  · 16 年前

    由于结构是值类型,因此其数据是 复制的 当作为参数传入方法时。例子:

    int someInt = 7;
    
    DoSomeMethod(someInt); // <-- This is passing the "value" 7.
    

    到目前为止,很容易理解,你可能想知道我的问题是如何有效的…因此,请考虑以下内容:

    public struct TimmysStructOfGoodness
    {
        public int SomeInt1;
        public int SomeInt2;
        public int SomeInt3;
        // ... later that day ...
        public int SomeInt999;
    }
    

    然后,参考以下代码:

    TimmysStructOfGoodness someStructOfGoodness = new blah blah blah...
    
    DoSomeMethod(someStructOfGoodness); // <-- **HERE IS WHERE THE QUESTION APPLIES!**
    

    上面的语句是否试图分配几个内存兆来“复制”我的值类型(struct)?

    如果答案是肯定的,那么“更快”和“较慢”之间的线是什么时候/在哪里?

    如果没有-那为什么不呢?因为我所知道的值类型,这应该是个问题。

    主要免责声明:我知道这与你为什么要使用结构对类没有任何关系,我知道我永远不会用999个字段构建结构-这只是一个关于底层内部结构和内部结构之类的问题:)

    5 回复  |  直到 16 年前
        1
  •  12
  •   Jon B    16 年前

    (更新,感谢其他用户的贡献)

    与类不同,结构是在堆栈上创建的。因此,实例化(并销毁)结构比类更快。

    除非(正如亚当·罗宾逊指出的那样)结构是一个类成员,在这种情况下,它和其他一切一起被分配到堆中。

    另一方面,每次您分配一个结构或将它传递给一个函数时,它都会被复制。

    我认为结构大小没有严格的限制。数千字节绝对是太多了。材料安全数据网 says :

    除非你需要参考类型 语义,一个更小的类 超过16个字节可能更有效 由系统作为结构处理。

    这是-如果您需要通过引用传递它,请将其作为一个类,无论大小如何。

    第二种想法是,只需在函数参数列表中指定ref,就可以通过引用传递struct。

    因此,如果通过引用传递一个大结构并将其用作类成员,那么它实际上是可以的。

        2
  •  9
  •   Mark Byers    16 年前

    结构和类不仅仅具有不同的性能,它们的行为也不同。您应该使用最适合您正在实现的类型的类型。何时使用其中一个或另一个的指导原则是 clearly described on MSDN .仅当以下所有条件都适用时才使用结构:

    • 它在逻辑上表示单个值,类似于基元类型(integer、double等)。
    • 它的实例大小小于16个字节。
    • 它是不变的。
    • 它不必经常装箱。

    如果要将结构传递给函数,则将获取该结构的副本。如果您的结构很大,那么它将导致复制大量数据。引用通常实现为4个字节(在x86上),因此传递对象只需要复制这4个字节。

    还要注意,上面的指导原则要求结构很小。

        3
  •  6
  •   LBushkin    16 年前

    结构的性能含义完全取决于您如何使用这种结构。

    对于结构是否比引用类型更快或更慢,不可能做出一个分类声明。这完全取决于您如何使用它们,以及在什么上下文中使用它们。

    结构(通常是值类型)最终可以分配到堆栈或堆上,这取决于它们的声明上下文。如果您在方法体中声明它(并且不显式地将其装箱),那么结构将结束在堆栈上。然后,如果您将该结构传递给一个按值接受它的方法(如您的示例中所示),那么它确实会被复制——但会被复制到堆栈上。堆栈上的分配是一个非常有效的过程(btw,.net堆也是非常有效的),但它是一个复制过程。

    当然,您可以使用ref/out传递结构-在这种情况下不会发生复制-对结构的引用将传递给该方法。这可能是可取的,也可能不是可取的,因为它将允许被调用的方法更改结构的内容。

    声明为类成员的结构实际上将在堆上分配(作为类的内存布局的一部分)。如果传递该类,则不会复制该结构,但仍可以通过类引用访问该结构。

    还可以通过显式装箱将结构获取到堆中:

    object x = new MyStruct( ... );  // boxed on the heap
    

    乔恩·斯基特有一个很好的 article about where things end up in memory 你应该读的。

        4
  •  3
  •   Charles Bretana    16 年前

    要增加混乱,请记住,如果不需要将副本传递给另一个方法,则可以始终通过引用传递结构以避免生成副本…

        5
  •  2
  •   Itay Maman    16 年前

    你的问题的答案是:是的。定义结构时,每个赋值都会引发一个复制操作。

    不存在将“fast”与“slow”分开的确切行,因为有许多因素会影响结构的速度WRT,例如:

    <校正; 结构按值存储:在堆栈上或包含对象内。因此,它们提升了缓存位置,因为相关页面已经加载到CPU缓存中的可能性更高,而任意堆页面可能不会加载到CPU缓存中。</correction>此外,结构不允许方法重写,因此方法调度也(稍微)更快。

    另一方面,每个任务都会产生一个副本。因此,如果您的代码有很多分配,那么上面提到的优势将被复制操作的成本所抵消。盈亏平衡点在程序之间是不同的(由于缓存位置、方法调度和分配的不同特性)