何时使用结构? [英] When to use struct?

查看:131
本文介绍了何时使用结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当你应该使用结构而不是类在C#中?我的概念模型是结构在时间使用时,产品的只是一个值类型的集合。一种方法在逻辑上抱团他们都为一个整体。

我在这些规则这里(<少时href=\"http://webcache.googleusercontent.com/search?q=cache%3aUUMkgdlxPo4J%3awww.stackprinter.com/export%3Fservice%3Dstackoverflow%26question%3D521298%26printer%3Dfalse%26linktohome%3Dtrue+&cd=1&hl=da&ct=clnk&gl=dk\">cached):


  • 系统结构应该重新present单
    值。

  • 系统结构应该有一个记忆
    足迹小于16字节。

  • 系统结构不应该被改变后,
    创建。

做这些规则的工作?什么是一个结构的意思是语义?


解决方案

由OP引用来源有一定的可信度...但微软 - 什么是对结构的使用上的立场?我找到一些额外的微软学习,这里是我发现了什么:


  

如果考虑的实例定义一个类的结构,而不是
  型小且通常短命的或通常嵌入在
  其他对象。


  
  

除非类型有以下所有特点,不要定义一个结构:


  
  

      
  1. 这在逻辑上重新presents一个值,类似于原始类型(整数,双,等)。

  2.   
  3. 它有一个实例大小超过16个字节小。

  4.   
  5. 这是不可改变的。

  6.   
  7. 它将不必频繁装箱。

  8.   

微软一直违反这些规则

好吧,#2,#3反正。我们敬爱的词典有2内部结构:

  [StructLayout(LayoutKind.Sequential)] //默认结构
私人结构进入//&LT; TKEY,TValue&GT;
{
    //查看code。在*参考源
}[序列化,StructLayout(LayoutKind.Sequential)]
公共结构枚举:
    IEnumerator的&LT; KeyValuePair&LT; TKEY的,TValue&GT;&gt;中IDisposable接口,
    IDictionaryEnumerator,IEnumerator的
{
    //查看code。在*参考源
}

* <一个href=\"http://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,d3599058f8d79be0\">Reference来源

在'JonnyCant code.com源得到了3开出4 - 相当原谅的,因为4号可能不会是一个问题。如果你发现自己拳击一个结构,重新考虑你的架构。

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


  1. 每个结构,输入枚举,再present单值。

  2. 速度

  3. 输入从不为Dictionary类之外的参数传递。进一步的调查显示,为了满足实现IEnumerable的的,字典使用枚举结构,它的副本每次请求枚举的时间......是有道理的。

  4. 内部的Dictionary类。 枚举是公众,因为字典是枚举的,并且必须有平等的无障碍IEnumerator接口实现 - 例如IEnumerator的吸气剂。

更新 - 此外,意识到,当一个结构实现一个接口 - 作为统计员那样 - 并且被映射到该实施类型,结构变得引用类型,并移动到堆。内部的Dictionary类,枚举的的仍然是值类型。然而,只要一个方法调用的GetEnumerator(),引用类型的IEnumerator 被返回。

我们在这里看不到任何企图或证明的要求,保持不变的结构或维持的只有16字节或实例的大小更小:


  1. 在以上声明的结构没有什么只读 - 不可以一成不变

  2. 这些结构的大小可能会超过16个字节

  3. 输入有一个未确定的寿命(从添加(),到删除() 清除(),或垃圾收集);

和...
 4.两个结构店TKEY的和TValue,我们都知道是非常能够被引用类型(额外的奖励信息)

散列尽管键,词典是快这部分是因为实例化一个结构比基准类型更快。在这里,我有一个词典&LT; INT,INT方式&gt; ,用于存储30万个随机整数与顺序递增键


  

容量:312874结果
  MEMSIZE:2660827字节结果
  已完成调整大小:5ms的结果
  总时间填写:889ms


容量:可用的内部数组必须被调整之前元素的个数。

MEMSIZE :通过序列化字典到MemoryStream并得到一个字节长度(对我们来说不够准确)决定。

已完成调整大小:它需要从150862元调整内部数组元素312874时间。当你的身影,每一个元素通过 Array.CopyTo连续复制(),这是不是太寒酸。

总时间填写:不可否认歪斜由于采伐和我加入到源的 onResize受到事件;但是,仍然IM pressive填补300K整数,而在操作过程中调整15次。只是出于好奇,什么会的总时间,以填补,如果我已经知道的能力如何? 13毫秒

所以,现在,如果输入是类?将这些时间或指标确实有所不同那么多吗?


  

容量:312874结果
  MEMSIZE:2660827字节结果
  已完成调整大小:26ms结果
  总时间填写:964ms


显然,最大的区别是在调整大小。如果字典的任何差与容量初始化?不够用... 12ms的有关。

什么情况是,因为输入是一个结构,它不需要初始化像一个引用类型。这是双方的美丽和价值型的祸根。为了使用输入为引用类型,我不得不插入以下code:

  / *
 *增加,以满足entry元素的初始化 -
 *这就是额外的时间都花在调整条目数组
 * ** /
的for(int i = 0; I&LT;素;我++)
{
    destinationArray [I] =新条目();
}
/ * * *********************************************** /

究其原因,我不得不初始化输入的每个数组元素为引用类型可以的 MSDN:结构设计。简而言之:


  

不要为结构的默认构造函数。


  
  

如果一个结构定义了一个默认的构造函数,当阵列
  创建结构,公共语言运行时自动
  执行每个数组元素的默认构造函数。


  
  

一些编译器,如C#编译器,不允许结构
  有默认的构造函数。


其实原因很简单,我们会从阿西莫夫的机器人三定律借


  1. 的结构必须是安全的使用

  2. 的结构必须有效地履行其职能,除非这会违反规则#1

  3. 的结构必须在其使用过程中,除非它的破坏,需要满足规则#1保持完好

... 做什么我们从这个带走的:简而言之,负责与使用价值类型。他们是快速而有效的,但有可能导致许多意想不到的行为,如果不好好保养(即无意份)的能力。

When should you use struct and not class in C#? My conceptual model is that structs are used in times when the item is merely a collection of value types. A way to logically hold them all together into a cohesive whole.

I came across these rules here (cached):

  • A struct should represent a single value.
  • A struct should have a memory footprint less than 16 bytes.
  • A struct should not be changed after creation.

Do these rules work? What does a struct mean semantically?

解决方案

The source referenced by the OP has some credibility ...but what about Microsoft - what is the stance on struct usage? I sought some extra learning from Microsoft, and here is what I found:

Consider defining a structure instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.

Do not define a structure unless the type has all of the following characteristics:

  1. It logically represents a single value, similar to primitive types (integer, double, and so on).
  2. It has an instance size smaller than 16 bytes.
  3. It is immutable.
  4. It will not have to be boxed frequently.

Microsoft consistently violates those rules

Okay, #2 and #3 anyway. Our beloved dictionary has 2 internal structs:

[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
}

*Reference Source

The 'JonnyCantCode.com' source got 3 out of 4 - quite forgivable since #4 probably wouldn't be an issue. If you find yourself boxing a struct, rethink your architecture.

Let's look at why Microsoft would use these structs:

  1. Each struct, Entry and Enumerator, represent single values.
  2. Speed
  3. Entry is never passed as a parameter outside of the Dictionary class. Further investigation shows that in order to satisfy implementation of IEnumerable, Dictionary uses the Enumerator struct which it copies every time an enumerator is requested ...makes sense.
  4. Internal to the Dictionary class. Enumerator is public because Dictionary is enumerable and must have equal accessibility to the IEnumerator interface implementation - e.g. IEnumerator getter.

Update - In addition, realize that when a struct implements an interface - as Enumerator does - and is cast to that implemented type, the struct becomes a reference type and is moved to the heap. Internal to the Dictionary class, Enumerator is still a value type. However, as soon as a method calls GetEnumerator(), a reference-type IEnumerator is returned.

What we don't see here is any attempt or proof of requirement to keep structs immutable or maintaining an instance size of only 16 bytes or less:

  1. Nothing in the structs above is declared readonly - not immutable
  2. Size of these struct could be well over 16 bytes
  3. Entry has an undetermined lifetime (from Add(), to Remove(), Clear(), or garbage collection);

And ... 4. Both structs store TKey and TValue, which we all know are quite capable of being reference types (added bonus info)

Hashed keys notwithstanding, dictionaries are fast in part because instancing a struct is quicker than a reference type. Here, I have a Dictionary<int, int> that stores 300,000 random integers with sequentially incremented keys.

Capacity: 312874
MemSize: 2660827 bytes
Completed Resize: 5ms
Total time to fill: 889ms

Capacity: number of elements available before the internal array must be resized.

MemSize: determined by serializing the dictionary into a MemoryStream and getting a byte length (accurate enough for our purposes).

Completed Resize: the time it takes to resize the internal array from 150862 elements to 312874 elements. When you figure that each element is sequentially copied via Array.CopyTo(), that ain't too shabby.

Total time to fill: admittedly skewed due to logging and an OnResize event I added to the source; however, still impressive to fill 300k integers while resizing 15 times during the operation. Just out of curiosity, what would the total time to fill be if I already knew the capacity? 13ms

So, now, what if Entry were a class? Would these times or metrics really differ that much?

Capacity: 312874
MemSize: 2660827 bytes
Completed Resize: 26ms
Total time to fill: 964ms

Obviously, the big difference is in resizing. Any difference if Dictionary is initialized with the Capacity? Not enough to be concerned with ... 12ms.

What happens is, because Entry is a struct, it does not require initialization like a reference type. This is both the beauty and the bane of the value type. In order to use Entry as a reference type, I had to insert the following code:

/*
 *  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( );
}
/*  *********************************************** */  

The reason I had to initialize each array element of Entry as a reference type can be found at MSDN: Structure Design. In short:

Do not provide a default constructor for a structure.

If a structure defines a default constructor, when arrays of the structure are created, the common language runtime automatically executes the default constructor on each array element.

Some compilers, such as the C# compiler, do not allow structures to have default constructors.

It is actually quite simple and we will borrow from Asimov's Three Laws of Robotics:

  1. The struct must be safe to use
  2. The struct must perform its function efficiently, unless this would violate rule #1
  3. The struct must remain intact during its use unless its destruction is required to satisfy rule #1

...what do we take away from this: in short, be responsible with the use of value types. They are quick and efficient, but have the ability to cause many unexpected behaviors if not properly maintained (i.e. unintentional copies).

这篇关于何时使用结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆