首页 > C# > 什么时候应该在 C# 中使用结构而不是类?

什么时候应该在 C# 中使用结构而不是类?

上一篇 下一篇

什么时候应该在 C# 中使用结构而不是类?我的概念模型是,当项目只是值类型的集合时,使用结构。一种在逻辑上将它们结合在一起形成一个有凝聚力的整体的方法。

我在这里遇到了这些规则:

  • 结构应表示单个
    值。
  • 结构的内存
    占用量应小于 16 个字节。
  • 结构在创建后
    不应更改。

这些规则有效吗?结构在语义上是什么意思?

分割线

网友回答:

每当您:

  1. 不需要多态性,
  2. 想要值语义,以及
  3. 希望避免堆分配和关联的垃圾回收开销。

然而,需要注意的是,结构体(任意大)比类引用(通常是一个机器字)更昂贵,因此类在实践中可能会更快。

分割线

网友回答:

OP引用的消息来源具有一定的可信度…但是微软呢——对结构使用的立场是什么?我向微软寻求一些额外的学习,这是我的发现:

如果
类型的实例很小且通常生存期较短或通常嵌入到其他对象中
,请考虑定义结构而不是类。

除非类型具有以下所有特征,否则不要定义结构:

  1. 它在逻辑上表示单个值,类似于基元类型(整数、双精度等)。
  2. 它的实例大小小于 16 字节。
  3. 它是不可变的。
  4. 它不必经常装箱。

微软一直违反这些规则

好的,无论如何#2和#3。我们心爱的词典有 2 个内部结构:

[StructLayout(LayoutKind.Sequential)]  // default for structs
private struct Entry  //<Tkey, TValue>
{
    //  View code at *Reference Source
}

[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator : 
    IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, 
    IDictionaryEnumerator, IEnumerator
{
    //  View code at *Reference Source
}

*参考资料来源

“JonnyCantCode.com”来源得到了 3 分中的 4 分——这是可以原谅的,因为 #4 可能不是问题。如果您发现自己在装箱结构,请重新考虑您的架构。

让我们看看为什么微软会使用这些结构:

  1. 每个结构 和 表示单个值。EntryEnumerator
  2. 速度
  3. Entry永远不会作为参数传递到字典类之外。进一步的调查表明,为了满足IEnumerable的实现,字典使用每次请求枚举器时复制的结构…意义。Enumerator
  4. 字典类的内部。 是公共的,因为字典是可枚举的,并且必须对 IEnumerator 接口实现具有相同的可访问性 – 例如 IEnumerator getter。Enumerator

更新 – 此外,请注意,当结构实现接口(如枚举器所做的那样)并强制转换为该实现的类型时,该结构将成为引用类型并移动到堆中。在字典类内部,枚举器仍然是值类型。但是,一旦方法调用 ,就会返回引用类型。GetEnumerator()IEnumerator

我们在这里看不到的是保持结构不可变或保持实例大小仅为 16 字节或更小的任何尝试或证明:

  1. 上述结构中的任何内容都没有声明 – 可变readonly
  2. 这些结构的大小可能远远超过 16 字节
  3. Entry具有不确定的生存期(从 、 到 、 或垃圾回收);Add()Remove()Clear()

和。。。
4.两种结构都存储了TKey和TValue,我们都知道它们非常有能力成为参考类型(添加奖励信息)

尽管有哈希键,但字典速度很快,部分原因是实例化结构比引用类型更快。在这里,我有一个存储 300,000 个随机整数和顺序递增键。Dictionary<int, int>

容量:312874
内存大小:2660827字节
完成调整大小:5ms 总填充时间:889ms

容量:在必须调整内部数组大小之前可用的元素数。

MemSize:通过将字典序列化为 MemoryStream 并获取字节长度(对于我们的目的来说足够精确)来确定。

已完成调整大小:将内部数组的大小从150862元素调整为312874元素所需的时间。当您认为每个元素都是通过 按顺序复制的时,这并不太破旧。Array.CopyTo()

填充总时间:由于日志记录和我添加到源的事件,不可否认地存在偏差;但是,在操作过程中调整大小 300 次时填充 15k 整数仍然令人印象深刻。只是出于好奇,如果我已经知道容量,那么要填补的总时间是多少?13毫秒OnResize

那么,现在,如果是一个班级呢?这些时间或指标真的会有那么大的差异吗?Entry

容量:312874
内存大小:2660827字节
完成调整大小:26ms 总填充时间:964ms

显然,最大的区别在于调整大小。如果使用容量初始化字典有什么区别?不够关心…12毫秒

发生的情况是,因为它是一个结构,所以它不需要像引用类型那样初始化。这既是价值类型的美丽,也是价值类型的祸根。为了用作引用类型,我必须插入以下代码:EntryEntry

/*
 *  Added to satisfy initialization of entry elements --
 *  this is where the extra time is spent resizing the Entry array
 * **/
for (int i = 0 ; i < prime ; i++)
{
    destinationArray[i] = new Entry( );
}
/*  *********************************************** */  

我必须初始化每个数组元素作为引用类型的原因可以在 MSDN:结构设计中找到。总之:Entry

不要为结构提供默认构造函数。

如果结构定义了默认构造函数,则在创建
结构的数组时,公共语言运行库会自动
对每个数组元素执行默认构造函数。

某些编译器(如 C# 编译器)不允许结构
具有默认构造函数。

这其实很简单,我们将借用阿西莫夫的机器人三定律

  1. 结构必须安全使用
  2. 结构必须有效地执行其功能,除非这会违反规则 #1
  3. 结构在使用过程中必须保持完整,除非需要销毁以满足规则 #1

我们从中得到什么:简而言之,对值类型的使用负责。它们快速高效,但如果维护不当(即无意的副本),可能会导致许多意外行为。

分割线

网友回答:

我不同意原始帖子中给出的规则。以下是我的规则:

  1. 当存储在数组中时,您可以使用结构来提高性能。(另请参阅结构体何时是答案?
  2. 在将结构化数据传入/从 C/C++传递的代码中需要它们
  3. 除非需要,否则不要使用结构:
    • 它们在赋值下和
      作为参数传递时的行为与“普通对象”(引用类型)不同,这可能会导致意外行为;
      如果查看代码
      的人不知道他们正在处理结构,则尤其危险。
    • 它们不能被继承。
    • 将结构作为参数传递比类更昂贵。

模板简介:该模板名称为【什么时候应该在 C# 中使用结构而不是类?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 245次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-13
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信文章 微信模板 微信公众平台 响应式 微信素材 单页式简历模板 html5 企业网站 微信图片 自适应
您可能会喜欢的其他模板