1

我知道拳击是一个流行的概念,有很多可用的信息,但我有几个问题我无法真正找到答案:

1)如果装箱导致值类型(结构)被转换为对象(引用类型)或引用类型,那么为什么要使用将被装箱并导致性能损失的值类型?我知道结构或类在某些情况下的好处和适用性。据说 (1) 值(值类型)倾向于在堆栈中的一个临时存储空间中存在,但要多长时间?如果我不需要该类型,我如何确保它在那个时候得到照顾和处置?或者这就是一次性模式发挥作用的地方?我认为使用结构的原因将是由于它的好处。

有趣的是,如果我使用一个结构来存储两个字符串和一个 DateTime 字段,那么该结构将同时保存两个引用(字符串)和 DateTime。我显然认为这比分散的值要快。在这个设计中有什么我需要注意的吗?(2)。

1) http://en.csharp-online.net/Classes, Structs, and Objects——装箱和拆箱

2) http://dotnetperls.com/Content/Struct-Examples.aspx

我在这里搜索了我想要的答案,但没有运气。我通常在这个站点上搜索诸如 GC、泛型、异常处理等主题,因为有很多智慧可以学习和分享。

感谢所有海报的(潜在)教育!请原谅任何潜在的天真。学习内部结构让我很好地花一些时间来理解 IL 等(很快要解决的问题)。

4

4 回答 4

4

如果您从不将值类型传递给引用变量,则不会发生装箱。当您不知道时,请回答以下问题

  • 像原始类型一样工作。
  • 实例大小小于 16 字节。
  • 是不可变的。
  • 值语义是可取的。

我通常也会考虑这样一个变量的生命周期。如果它是在方法中使用的局部变量,那么我会倾向于使用结构(否则是类)。

于 2009-03-06T22:36:50.537 回答
1

您应该使用值类型,因为它们具有逻辑优势,而不是性能提升。话虽这么说,因为值类型是在堆栈上管理的,不需要参与垃圾回收。如果您有一个不断创建和丢弃的类型(如 int、float、double 等),那么您可以通过将它们转换为结构来获得良好的提升。需要注意的是,如果你也可以使结构不可变,你应该真正考虑这一点。

于 2009-03-06T22:35:59.917 回答
1

需要考虑的其他几件事 -

首先,您要确保结构是不可变的(通常)。因此,没有包含引用类型的结构是一个很好的经验法则。字符串可能是一个例外,因为它们在 C# 中是不可变的,但就设计的通用经验法则而言,我会对此保持警惕。

其次,到目前为止还没有提到结构的另一个用例——大量的小对象。如果您有一个大列表或小对象数组,结构提供了显着更好的缓存一致性,并且绝对至关重要。这就是为什么大多数 3D 引擎使用结构作为点/向量的原因——它们往往有大量的点数组作为顶点等。

如果性能是您的应用程序的重要组成部分,这是值得关注的。例如,在我的一个应用程序中,将单一类型从类更改为结构体可以将长时间运行(> 5 分钟运行时间)的进程减少 40%。如果您在繁重的数学计算中重复使用它们,则将对象放在内存中可以提供巨大的收益。

现在 - 在您的情况下,拥有 2 个字符串和一个 DateTime 可能不会看到任何改进。适用于字符串的例程类型可能不会进行繁重的计算(希望如此),即:在空间中转换 50 万个点,或进行大型矩阵解等。

最后 - 您会注意到 .net3.5sp1 使结构更加有用。在 3.5sp1 之前(在 x86 上),没有内联结构调用的方法。这限制了通过结构可能获得的性能提升。更新您的框架可以使旧的结构代码更快(在某些情况下)。

于 2009-03-07T02:10:55.233 回答
0

您并不总是需要拳击,而对于泛型,几乎不需要拳击。
值类型(结构是值类型)使用的内存将
在方法结束/返回后立即被占用,您
无需为此做任何事情。

声明为实例成员的值类型将在内存中,直到对象
被 GC 删除。

引用类型保存在托管堆上。 当没有对象持有对它的引用时,
在方法中实例化的引用类型将被垃圾收集器删除。

GC 自己工作,在大多数情况下,您应该不理会它。
您无法预测对象何时会被 GC 删除。

Dispose 模式用于引用类型,但不会强制 GC 删除
对象。它通常用于释放非托管资源。

对于堆栈中的值,请考虑以下事项:
假设您有一个具有三个方法的简单程序,如下所示:
当该程序运行时,Main 方法将运行,等等。请遵循以下
数字:


Main
{
   // (0) Stack is empty
   int firstInt = 0;
   // (1) Stack now contains:
   //                     firstInt
   DoSomething1();
   // (7) Stack still contains:
   //                     firstInt
}
// Program ends

DoSomething()
{
   int anInteger = 0; 
   // (2) Stack now contains:
   //                    anInteger
   //                    firstInt
   DoMore()
   // (5) Stack now contains:
   //                     anInteger
   //                     firstInt
}
// (6) anInteger goes out of scope

DoMore
{
  int anotherInteger = 1; 
   // (3) Stack now contains:
   //                     anotherInteger
   //                     anInteger
   //                     firstInt
}
// (4) anotherInteger goes out of scope

于 2009-03-06T23:23:15.637 回答