Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
208 views
in Technique[技术] by (71.8m points)

c# - 何时使用struct?(When to use struct?)

When should you use struct and not class in C#?

(什么时候应该在C#中使用struct而不是class?)

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 :

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

  • A struct should represent a single value.

    (结构应该表示单个值。)

  • A struct should have a memory footprint less than 16 bytes.

    (结构应具有小于16个字节的内存占用。)

  • A struct should not be changed after creation.

    (创建后不应更改结构。)

Do these rules work?

(这些规则有效吗?)

What does a struct mean semantically?

(结构在语义上意味着什么?)

  ask by Alex Baranosky translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The source referenced by the OP has some credibility ...but what about Microsoft - what is the stance on struct usage?

(OP引用的来源具有一定的可信度......但是微软怎么样 - 结构使用的立场是什么?)

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.

    (它的实例大小小于16个字节。)

  3. It is immutable.

    (这是不可改变的。)

  4. It will not have to be boxed frequently.

    (它不必经常装箱。)

Microsoft consistently violates those rules (微软一直违反这些规则)

Okay, #2 and #3 anyway.

(好的,无论如何,#2和#3。)

Our beloved dictionary has 2 internal structs:

(我们心爱的字典有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
}

* Reference Source

(* 参考资料来源)

The 'JonnyCantCode.com' source got 3 out of 4 - quite forgivable since #4 probably wouldn't be an issue.

('JonnyCantCode.com'来源获得了4分中的3分 - 相当可原谅,因为#4可能不会成为问题。)

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.

    (每个struct, EntryEnumerator代表单个值。)

  2. Speed

    (速度)

  3. Entry is never passed as a parameter outside of the Dictionary class.

    (Entry永远不会作为Dictionary类之外的参数传递。)

    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.

    (进一步的研究表明,为了满足IEnumerable的实现,Dictionary使用Enumerator结构,它每次请求枚举器时都会复制...这是有意义的。)

  4. Internal to the Dictionary class.

    (Dictionary类的内部。)

    Enumerator is public because Dictionary is enumerable and must have equal accessibility to the IEnumerator interface implementation - eg IEnumerator getter.

    (Enumerator是公共的,因为Dictionary是可枚举的,并且必须具有与IEnumerator接口实现相同的可访问性 - 例如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.

(更新 - 此外,要意识到当一个struct实现一个接口 - 就像Enumerator那样 - 并且被强制转换为该实现的类型时,该struct将成为一个引用类型并被移动到堆中。)

Internal to the Dictionary class, Enumerator is still a value type.

(内部的Dictionary类,枚举仍然一个值类型。)

However, as soon as a method calls GetEnumerator() , a reference-type IEnumerator is returned.

(但是,只要方法调用GetEnumerator() ,就会返回引用类型的IEnumerator 。)

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:

(我们在这里看不到的任何尝试或证明要求保持结构不可变或维持实例大小只有16个字节或更少:)

  1. Nothing in the structs above is declared readonly - not immutable

    (上面的结构中没有任何内容被声明为readonly - 不是一成不变的)

  2. Size of these struct could be well over 16 bytes

    (这些结构的大小可能超过16个字节)

  3. Entry has an undetermined lifetime (from Add() , to Remove() , Clear() , or garbage collection);

    (Entry具有未确定的生命周期(从Add()Remove()Clear()或垃圾收集);)

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

(并且... 4.两个结构存储TKey和TValue,我们都知道它们很有能力作为参考类型(添加奖励信息))

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.

(在这里,我有一个Dictionary<int, int> ,它存储300,000个带有顺序递增键的随机整数。)

Capacity: 312874

(容量:312874)
MemSize: 2660827 bytes

(MemSize:2660827字节)
Completed Resize: 5ms

(完成调整大小:5ms)
Total time to fill: 889ms

(总时间: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).

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

Completed Resize : the time it takes to resize the internal array from 150862 elements to 312874 elements.

(已完成调整大小 :将内部数组从150862元素调整为312874元素所需的时间。)

When you figure that each element is sequentially copied via Array.CopyTo() , that ain't too shabby.

(当你想通过Array.CopyTo()顺序复制每个元素时,这不是太破旧。)

Total time to fill : admittedly skewed due to logging and an OnResize event I added to the source;

(填充的总时间 :由于记录和我添加到源中的OnResize事件而被认为是倾斜的;)

however, still impressive to fill 300k integers while resizing 15 times during the operation.

(然而,在操作期间调整15次时,仍然令人印象深刻地填充300k整数。)

Just out of curiosity, what would the total time to fill be if I already knew the capacity?

(出于好奇,如果我已经知道容量,那么总的时间是多少?)

13ms

(13毫秒)

So, now, what if Entry were a class?

(那么,现在,如果Entry是一个班级呢?)

Would these times or metrics really differ that much?

(这些时间或指标真的会有那么大差异吗?)

Capacity: 312874

(容量:312874)
MemSize: 2660827 bytes

(MemSize:2660827字节)
Completed Resize: 26ms

(完成调整大小:26ms)
Total time to fill: 964ms

(总时间:964ms)

Obviously, the big difference is in resizing.

(显然,最大的区别在于调整大小。)

Any difference if Dictionary is initialized with the Capacity?

(如果使用容量初始化Dictionary,会有什么不同吗?)

Not enough to be concerned with ... 12ms .

(不足以关注... 12ms 。)

What happens is, because Entry is a struct, it does not require initialization like a reference type.

(会发生什么,因为Entry是一个结构,它不需要像引用类型那样进行初始化。)

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:

(为了使用Entry作为引用类型,我必须插入以下代码:)

/*
 *  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 .

(我必须将Entry每个数组元素初始化为引用类型的原因可以在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.

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

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

    (结构必须有效地执行其功能,除非这违反规则#1)

  3. The struct must remain intact during its use unless its destruction is required to satisfy rule #1

    (结构在使用过程中必须保持完整,除非要求销毁以满足规则#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 (ie unintentional copies

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...