当前位置:  开发笔记 > 编程语言 > 正文

何时使用struct?

如何解决《何时使用struct?》经验,为你挑选了21个好方法。

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

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

结构应该表示单个值.

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

创建后不应更改结构.

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



1> IAbstract..:

OP引用的来源具有一定的可信度......但是微软怎么样 - 结构使用的立场是什么?我从微软那里寻求了一些额外的学习,这就是我发现的:

如果类型的实例很小并且通常是短暂的或者通常嵌入在其他对象中,则考虑定义结构而不是类.

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

    它逻辑上表示单个值,类似于基本类型(整数,双精度等).

    它的实例大小小于16个字节.

    这是不可改变的.

    它不必经常装箱.

微软一直违反这些规则

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

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

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

*参考资料来源

'JonnyCantCode.com'来源获得了4分中的3分 - 相当可原谅,因为#4可能不会成为问题.如果你发现自己装了一个结构,重新考虑你的架构.

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

    每个结构EntryEnumerator,代表单个值.

    速度

    Entry永远不会作为Dictionary类之外的参数传递.进一步的研究表明,为了满足IEnumerable的实现,Dictionary Enumerator每次使用枚举器时都使用它复制的结构......这是有意义的.

    Dictionary类的内部.Enumerator是公共的,因为Dictionary是可枚举的,并且必须具有与IEnumerator接口实现相同的可访问性 - 例如IEnumerator getter.

更新 - 此外,要意识到当一个struct实现一个接口 - 就像Enumerator那样 - 并且被强制转换为该实现的类型时,该struct将成为一个引用类型并被移动到堆中.内部的Dictionary类,枚举仍然值类型.但是,只要方法调用GetEnumerator(),IEnumerator就会返回引用类型.

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

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

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

    Entry具有不确定的寿命(从Add(),到Remove(),Clear()或垃圾收集);

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

尽管有散列键,但字典很快部分是因为实例化结构比引用类型更快.在这里,我有一个Dictionary存储300,000个随机整数和顺序递增的键.

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

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

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

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

填充的总时间:由于记录和OnResize我添加到源中的事件而导致的倾斜; 然而,在操作期间调整15次时,仍然令人印象深刻地填充300k整数.出于好奇,如果我已经知道容量,那么总的时间是多少?13毫秒

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

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

显然,最大的区别在于调整大小.如果使用容量初始化Dictionary,会有什么不同吗?不足以关注... 12ms.

会发生什么,因为它Entry是一个结构,它不需要像引用类型那样初始化.这既是价值类型的美丽又是祸根.为了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( );
}
/*  *********************************************** */  

我必须将每个数组元素初始化Entry为引用类型的原因可以在MSDN:Structure Design中找到.简而言之:

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

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

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

它实际上非常简单,我们将借用阿西莫夫的三机器人法则:

    结构必须安全使用

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

    结构在使用过程中必须保持完整,除非要求销毁以满足规则#1

... 我们从中得到什么:简而言之,对价值类型的使用负责.它们快速有效,但如果维护不当(即无意复制),则有能力引发许多意外行为.


请记住[readonly!= immutable.](http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability. ASPX)
至于微软的规则,关于不变性的规则似乎旨在阻止使用值类型,使得它们的行为与引用类型的行为不同,尽管*分段可变值语义可能是有用的*.如果类型是分段可变的将使其更容易使用,并且如果该类型的存储位置应该在逻辑上彼此分离,则该类型应该是"可变"结构.
@IAbstract:有些人会认为`Dictionary`条目类型是合理的,因为它只是一个内部类型,性能被认为比语义或其他一些借口更重要.我的观点是像`Rectangle`这样的类型应该将其内容暴露为可单独编辑的字段而不是"因为"性能优势超过了所产生的语义缺陷,而是因为*类型在语义上表示一组固定的独立值*,所以可变结构在性能和语义上都更优越*.
Microsoft的许多类型都违反这些规则的事实并不表示这些类型存在问题,而是表明这些规则不应应用于所有结构类型。如果一个结构表示单个实体(例如使用Decimal或DateTime),那么如果它不遵守其他三个规则,则应将其替换为一个类。如果结构拥有固定的变量集合,每个变量都可以拥有对其类型有效的任何值[例如:矩形],则它应遵守*不同*规则,其中一些规则与“单值”结构。
@supercat:我同意......而我的回答的全部意义在于,"指南"非常弱,结构应该充分了解并理解行为.在这里查看我对可变结构的回答:http://stackoverflow.com/questions/8108920/why-is-it-okay-that-this-struct-is-mutable-when-are-mutable-structs-acceptable/8109254# 8109254

2> dsimcha..:

每当您不需要多态时,需要值语义,并希望避免堆分配和相关的垃圾收集开销.然而,需要注意的是,结构(任意大)传递比类引用(通常是一个机器字)更昂贵,因此类在实践中最终会更快.



3> ILoveFortran..:

我不同意原帖中给出的规则.这是我的规则:

1)存储在数组中时使用结构体来提高性能.(另见结构何时?)

2)您需要将代码传递给C/C++的结构化数据

3)除非您需要,否则不要使用结构:

它们与赋值下的"普通对象"(引用类型)和作为参数传递时的行为不同,这可能导致意外行为; 如果查看代码的人不知道他们正在处理结构,那么这尤其危险.

他们不能继承.

将结构作为参数传递比类更昂贵.


+1是的,我完全同意#1(这在处理图像等事情时是一个巨大的*优势)并且指出它们*与"普通对象"不同*并且有*知道知道的方式这*除现有知识或检查类型本身.此外,您不能将null值强制转换为结构类型:-)这实际上是一种情况,我*几乎*希望非核心值类型有一些'匈牙利'或变量强制'struct'关键字报关网站.
继承很少是工作的正确工具,如果不进行分析就过多地推理性能是一个坏主意.首先,结构可以通过引用传递.其次,通过引用或按值传递很少是一个重要的性能问题.最后,您没有考虑需要为类进行的额外堆分配和垃圾回收.就个人而言,我更喜欢将结构视为普通旧数据和类作为*做*事物(对象)的事物,尽管你也可以在结构上定义方法.

4> JoshBerke..:

当您需要值语义而不是引用语义时,请使用结构.

编辑

不确定为什么大家都在贬低这一点,但这是一个有效的观点,并且在操作澄清他的问题之前做出,这是结构的最根本的基本原因.

如果需要引用语义,则需要一个类而不是结构.


@Josh:对于那些不知道它的人来说,简单地说这是一个不充分的答案,因为他们很可能也不知道这意味着什么.
不是每个人都知道.这显然是一个例子.
这是最基本的案例,应该为阅读这篇文章并且不知道这一点的任何人说明.
每个人都知道.看起来他正在寻找的不仅仅是"结构是价值类型"的答案.
不是说这个答案不是真的; 它显然是.这不是重点.

5> Marc Gravell..:

除了"它是一个值"的答案之外,使用结构的一个特定方案是,当您知道有一组导致垃圾收集问题的数据时,您有很多对象.例如,Person实例的大型列表/数组.这里的自然隐喻是一个类,但是如果你有大量长寿的Person实例,它们最终会堵塞GEN-2并导致GC停顿.如果场景保证,这里的一种可能的方法是使用Person 结构的数组(不是列表),即Person[].现在,不是在GEN-2中拥有数百万个对象,而是在LOH上有一个块(我假设这里没有字符串等 - 即没有任何引用的纯值).这对GC影响很小.

使用这些数据很尴尬,因为数据可能超出了结构的大小,并且您不希望一直复制胖值.但是,直接在数组中访问它不会复制结构 - 它就位(与列表索引器相比,它会复制).这意味着很多工作与索引:

int index = ...
int id = peopleArray[index].Id;

请注意,保持值本身不可变将有助于此处.对于更复杂的逻辑,请使用带有by-ref参数的方法:

void Foo(ref Person person) {...}
...
Foo(ref peopleArray[index]);

同样,这是就地 - 我们没有复制价值.

在非常具体的情况下,这种策略可以非常成功; 但是,只有当你知道自己在做什么以及为什么这样做时,它才是一个相当先进的scernario.这里的默认值是一个类.


@RoyiNamir我对此也很好奇,但我相信答案在于Marc的第二段回答."但是,直接在数组中访问它不会复制结构 - 它就位(与列表索引器相比,它会复制)."
@MarcGravell你为什么提到:_use一个数组(不是列表)_?`List`我相信,在幕后使用`Array`.不?

6> bUKaneer..:

从C#语言规范:

1.7结构

与类一样,结构体是可以包含数据成员和函数成员的数据结构,但与类不同,结构体是值类型,不需要堆分配.结构类型的变量直接存储结构的数据,而类类型的变量存储对动态分配的对象的引用.结构类型不支持用户指定的继承,并且所有结构类型都隐式继承自类型对象.

结构对于具有值语义的小型数据结构特别有用.复数,坐标系中的点或字典中的键值对都是结构的好例子.对小型数据结构使用结构而不是类可以使应用程序执行的内存分配数量产生很大差异.例如,以下程序创建并初始化100个点的数组.将Point实现为类,实例化101个单独的对象 - 一个用于数组,一个用于100个元素.

class Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

class Test
{
   static void Main() {
      Point[] points = new Point[100];
      for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
   }
}

另一种方法是使Point成为一个结构.

struct Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

现在,只实例化一个对象 - 数组的对象 - 并且Point实例以串联方式存储在数组中.

使用new运算符调用Struct构造函数,但这并不意味着正在分配内存.结构构造函数只是返回结构值本身(通常在堆栈的临时位置),而不是动态分配对象并返回对它的引用,然后根据需要复制该值.

对于类,两个变量可以引用同一个对象,因此对一个变量的操作可能会影响另一个变量引用的对象.对于结构体,每个变量都有自己的数据副本,并且一个变量不可能影响另一个变量.例如,由以下代码片段生成的输出取决于Point是类还是结构.

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

如果Point是一个类,则输出为20,因为a和b引用同一个对象.如果Point是结构,则输出为10,因为a到b的赋值会创建值的副本,并且此副本不受后续分配到ax的影响

前面的例子强调了结构的两个局限性.首先,复制整个结构通常比复制对象引用效率低,因此对于结构而言,赋值和值参数传递可能比使用引用类型更昂贵.其次,除了ref和out参数之外,不可能创建对结构的引用,结构排除了它们在许多情况下的使用.


虽然对结构的引用不能持久的事实有时是一种限制,但它也是一个非常有用的特征..net的一个主要缺点是,没有可靠的方法将外部代码传递给可变对象的引用,而不会永远失去对该对象的控制.相比之下,人们可以安全地给外部方法一个`ref`到一个可变结构,并且知道外部方法将对它执行的任何突变都将在它返回之前完成.这太糟糕了.net没有任何短暂参数和函数返回值的概念,因为......
...这将允许使用类对象实现`ref`传递的结构的有利语义.本质上,局部变量,参数和函数返回值可以是可持久的(默认的),可返回的或短暂的.代码将被禁止将短暂的东西复制到任何比现在范围更长的东西.可回收的东西就像短暂的东西,除了它们可以从一个函数返回.函数的返回值将受适用于其任何"可返回"参数的最严格限制的约束.

7> Franci Penov..:

结构适用于数据的原子表示,其中所述数据可以由代码多次复制.克隆一个对象通常比复制一个结构更昂贵,因为它涉及分配内存,运行构造函数和完成它时解除分配/垃圾回收.


是的,但是大型结构可能比类引用更昂贵(当传递给方法时).

8> Usman Zafar..:

这是一个基本规则.

如果所有成员字段都是值类型,则创建结构.

如果任何一个成员字段是引用类型,请创建一个.这是因为引用类型字段无论如何都需要堆分配.

Exmaples

public struct MyPoint 
{
    public int X; // Value Type
    public int Y; // Value Type
}

public class MyPointWithName 
{
    public int X; // Value Type
    public int Y; // Value Type
    public string Name; // Reference Type
}


像`string`这样的不可变引用类型在语义上等价于值,并且将对不可变对象的引用存储到字段中不需要堆分配.具有公开字段的结构体和具有公开字段的类对象之间的区别在于给定代码序列`var q = p; 的pX = 4; qX = 5;`,如果`a`是结构类型,`pX`将具有值4,如果它是类类型则为5.如果希望能够方便地修改该类型的成员,则应根据是否需要更改"q"来选择"class"或"struct"来影响`p`.

9> BC...:

第一种:互操作方案或需要指定内存布局时

第二:当数据与参考指针的大小几乎相同时.



10> Maurice Flan..:

在需要使用StructLayoutAttribute显式指定内存布局的情况下,您需要使用"struct" - 通常用于PInvoke.

编辑:注释指出您可以使用StructLayoutAttribute的类或结构,这当然是正确的.在实践中,您通常会使用一个结构 - 它在堆栈和堆上分配,如果您只是将参数传递给非托管方法调用,这是有意义的.


StructLayoutAttribute可以应用于结构或类,因此这不是使用结构的原因.

11> mjfgates..:

我使用结构包装或解压缩任何种类的二进制通信格式.这包括读取或写入磁盘,DirectX顶点列表,网络协议或处理加密/压缩数据.

在此上下文中,您列出的三条准则对我没用.当我需要在特定顺序中写出四百个字节的东西时,我将定义一个四百字节的结构,并且我将填充它应该具有的任何不相关的值,并且我将要去以任何最有意义的方式设置它.(好吧,四百个字节会很奇怪 - 但是当我以Excel文件为生,我正在处理全部最多大约四十个字节的结构,因为那是BIFF记录的大小.)



12> leppie..:

除了运行时和其他各种用于PInvoke目的的值类型之外,您应该只在2个方案中使用valuetypes.

    当你需要复制语义时.

    当您需要自动初始化时,通常在这些类型的数组中.



13> Sujit..:

.NET支持value typesreference types(在Java中,您只能定义引用类型).reference types在托管堆中分配get的实例,并且在没有对它们的未完成引用时进行垃圾回收.value types另一方面,实例在其中分配stack,因此一旦其范围结束,就分配内存.当然,value types通过价值和reference types参考传递.除System.String外,所有C#原始数据类型都是值类型.

何时使用struct over class,

在C#中,structsvalue types,类是reference types.您可以使用enum关键字和struct关键字在C#中创建值类型.使用a value type而不是a reference type将导致托管堆上的对象更少,从而导致垃圾收集器(GC)上的负载更少,GC周期更少,从而提高性能.但是,value types也有它们的缺点.绕过一个大struct的肯定比通过引用更昂贵,这是一个明显的问题.另一个问题是与之相关的开销boxing/unboxing.如果您想知道什么boxing/unboxing意思,请按照这些链接获取有关boxing和的详细说明unboxing.除了性能之外,有时候你只需要类型来拥有值语义,如果reference types你拥有它们,这将是非常困难(或丑陋)的实现.您应该value types只使用,当您需要复制语义或需要自动初始化时,通常在arrays这些类型中.



14> Jason Willia..:

结构是一个值类型.如果将结构分配给新变量,则新变量将包含原始副本.

public struct IntStruct {
    public int Value {get; set;}
}

以下结果导致存储在内存中的结构的5个实例:

var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1;  // A copy is made
var struct3 = struct2;  // A copy is made
var struct4 = struct3;  // A copy is made
var struct5 = struct4;  // A copy is made

// NOTE: A "copy" will occur when you pass a struct into a method parameter.
// To avoid the "copy", use the ref keyword.

// Although structs are designed to use less system resources
// than classes.  If used incorrectly, they could use significantly more.

是引用类型.将类分配给新变量时,该变量包含对原始类对象的引用.

public class IntClass {
    public int Value {get; set;}
}

超出以下内容只会导致内存中类对象的一个实例.

var class1 = new IntClass() { Value = 0 };
var class2 = class1;  // A reference is made to class1
var class3 = class2;  // A reference is made to class1
var class4 = class3;  // A reference is made to class1
var class5 = class4;  // A reference is made to class1  

结构可能会增加代码错误的可能性.如果将值对象视为可变引用对象,则当所做的更改意外丢失时,开发人员可能会感到惊讶.

var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// At this point, a developer may be surprised when 
// struct1.Value is 0 and not 1



15> SnapJag..:

不 - 我不完全同意这些规则.它们是考虑性能和标准化的良好指导方针,但不是考虑到可能性.

正如您在回复中所看到的,有很多创造性的方法可以使用它们.因此,这些指南只需要这样,总是为了性能和效率.

在这种情况下,我使用类来表示更大形式的真实世界对象,我使用结构来表示具有更精确用途的较小对象.你说的方式,"一个更有凝聚力的整体." 关键词具有凝聚力.这些类将是更多面向对象的元素,而结构体可以具有一些这些特征,尽管规模较小.IMO.

我在Treeview和Listview标签中经常使用它们,可以非常快速地访问常见的静态属性.我一直在努力以另一种方式获取此信息.例如,在我的数据库应用程序中,我使用Treeview,其中包含表,SP,函数或任何其他对象.我创建并填充我的结构,将其放在标签中,将其拉出,获取选择的数据等等.我不会在上课时这样做!

我确实尝试将它们保持在小的状态,在单实例情况下使用它们,并防止它们发生变化.注意记忆,分配和性能是明智的.测试是必要的.



16> supercat..:

C#或其他.net语言中的结构类型通常用于保存应该像固定大小的值组一样的事物.结构类型的一个有用方面是结构类型实例的字段可以通过修改其所在的存储位置来修改,而不是以其他方式.可以以这样的方式对结构进行编码:改变任何字段的唯一方法是构造一个全新的实例,然后使用结构赋值通过用新实例中的值覆盖它们来改变目标的所有字段,但是除非struct没有提供创建其字段具有非默认值的实例的方法,否则如果struct本身存储在可变位置,则其所有字段都是可变的.

请注意,如果结构包含私有类类型字段,并且将其自己的成员重定向到包装类对象的成员,则可以设计结构类型,使其基本上表现得像类类型.例如,PersonCollection可能会提供性能SortedByNameSortedById,两者都持有"不可改变的"参考了PersonCollection(在其构造集),并实现GetEnumerator通过调用creator.GetNameSortedEnumeratorcreator.GetIdSortedEnumerator.这样的结构表现得很像a的引用PersonCollection,除了它们的GetEnumerator方法将被绑定到不同的方法PersonCollection.也可以有一个结构包装数组的一部分(例如,可以定义一个ArrayRange结构,该结构将保存一个T[]被调用的Arr,一个int Offset和一个intLength,索引属性,对于idx范围为0到的索引Length-1,将访问Arr[idx+Offset]).不幸的是,如果foo是这种结构的只读实例,当前的编译器版本将不允许这样的操作,foo[3]+=4;因为它们无法确定此类操作是否会尝试写入字段foo.

也可以设计一个结构,使其表现得像一个值类型,它保存一个可变大小的集合(每当结构都会被复制时),但唯一的方法就是确保没有对象. struct持有一个引用将暴露于任何可能会改变它的东西.例如,可以有一个类似于数组的结构,它包含一个私有数组,其索引的"put"方法创建一个新数组,其内容与原始数组的内容类似,除了一个更改的元素.不幸的是,使这种结构有效地执行可能有些困难.虽然有时结构语义可以很方便(例如,能够将类似数组的集合传递给例程,调用者和被调用者都知道外部代码不会修改集合,



17> Roman Pokrov..:

我用BenchmarkDotNet做了一个小基准,以便更好地理解数字中的"结构"效益.我正在测试循环遍历结构(或类)的数组(或列表).创建这些数组或列表超出了基准测试的范围 - 很明显,"类"更重将使用更多内存,并且将涉及GC.

所以结论是:小心LINQ和隐藏的结构装箱/拆箱,并使用结构进行微优化,严格保留数组.

PS关于通过调用堆栈传递struct/class的另一个基准是/sf/ask/17360801/

BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233542 Hz, Resolution=309.2584 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Core   : .NET Core 4.6.25211.01, 64bit RyuJIT


          Method |  Job | Runtime |      Mean |     Error |    StdDev |       Min |       Max |    Median | Rank |  Gen 0 | Allocated |
---------------- |----- |-------- |----------:|----------:|----------:|----------:|----------:|----------:|-----:|-------:|----------:|
   TestListClass |  Clr |     Clr |  5.599 us | 0.0408 us | 0.0382 us |  5.561 us |  5.689 us |  5.583 us |    3 |      - |       0 B |
  TestArrayClass |  Clr |     Clr |  2.024 us | 0.0102 us | 0.0096 us |  2.011 us |  2.043 us |  2.022 us |    2 |      - |       0 B |
  TestListStruct |  Clr |     Clr |  8.427 us | 0.1983 us | 0.2204 us |  8.101 us |  9.007 us |  8.374 us |    5 |      - |       0 B |
 TestArrayStruct |  Clr |     Clr |  1.539 us | 0.0295 us | 0.0276 us |  1.502 us |  1.577 us |  1.537 us |    1 |      - |       0 B |
   TestLinqClass |  Clr |     Clr | 13.117 us | 0.1007 us | 0.0892 us | 13.007 us | 13.301 us | 13.089 us |    7 | 0.0153 |      80 B |
  TestLinqStruct |  Clr |     Clr | 28.676 us | 0.1837 us | 0.1534 us | 28.441 us | 28.957 us | 28.660 us |    9 |      - |      96 B |
   TestListClass | Core |    Core |  5.747 us | 0.1147 us | 0.1275 us |  5.567 us |  5.945 us |  5.756 us |    4 |      - |       0 B |
  TestArrayClass | Core |    Core |  2.023 us | 0.0299 us | 0.0279 us |  1.990 us |  2.069 us |  2.013 us |    2 |      - |       0 B |
  TestListStruct | Core |    Core |  8.753 us | 0.1659 us | 0.1910 us |  8.498 us |  9.110 us |  8.670 us |    6 |      - |       0 B |
 TestArrayStruct | Core |    Core |  1.552 us | 0.0307 us | 0.0377 us |  1.496 us |  1.618 us |  1.552 us |    1 |      - |       0 B |
   TestLinqClass | Core |    Core | 14.286 us | 0.2430 us | 0.2273 us | 13.956 us | 14.678 us | 14.313 us |    8 | 0.0153 |      72 B |
  TestLinqStruct | Core |    Core | 30.121 us | 0.5941 us | 0.5835 us | 28.928 us | 30.909 us | 30.153 us |   10 |      - |      88 B |

码:

[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkRef
    {
        public class C1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        public struct S1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        List testListClass = new List();
        List testListStruct = new List();
        C1[] testArrayClass;
        S1[] testArrayStruct;
        public BenchmarkRef()
        {
            for(int i=0;i<1000;i++)
            {
                testListClass.Add(new C1  { Text1= i.ToString(), Text2=null, Text3= i.ToString() });
                testListStruct.Add(new S1 { Text1 = i.ToString(), Text2 = null, Text3 = i.ToString() });
            }
            testArrayClass = testListClass.ToArray();
            testArrayStruct = testListStruct.ToArray();
        }

        [Benchmark]
        public int TestListClass()
        {
            var x = 0;
            foreach(var i in testListClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayClass()
        {
            var x = 0;
            foreach (var i in testArrayClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestListStruct()
        {
            var x = 0;
            foreach (var i in testListStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayStruct()
        {
            var x = 0;
            foreach (var i in testArrayStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestLinqClass()
        {
            var x = testListClass.Select(i=> i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }

        [Benchmark]
        public int TestLinqStruct()
        {
            var x = testListStruct.Select(i => i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }
    }



18> rockXrock..:

我的规则是

1,始终使用课程;

2,如果存在任何性能问题,我会尝试根据@IAbstract提到的规则将某些类更改为struct,然后进行测试以查看这些更改是否可以提高性能.



19> J_hajian_nzd..:

类是引用类型.创建类的对象时,为其分配对象的变量仅保留对该内存的引用.将对象引用分配给新变量时,新变量引用原始对象.通过一个变量进行的更改会反映在另一个变量中,因为它们都引用相同的数据.结构是一种值类型.创建结构时,为其分配结构的变量保存结构的实际数据.将结构分配给新变量时,会复制该变量.因此,新变量和原始变量包含相同数据的两个单独副本.对一个副本所做的更改不会影响另一个副本.通常,类用于建模更复杂的行为,或者在创建类对象后要修改的数据.结构最适合于主要包含在创建结构后不打算修改的数据的小型数据结构.

类和结构(C#编程指南)



20> Brian..:

我认为一个很好的第一近似是“从不”。

我认为一个好的第二近似值是“从不”。

如果您迫切希望获得性能,请考虑使用它们,但是请务必进行测量。


您应该编辑您的帖子并详细说明您的观点-您已经给出了自己的观点,但是应该支持为什么要发表此观点。
我不同意这个答案。结构在许多情况下都有合法用途。这是一个示例-以原子方式封送数据跨进程。
我认为他们需要使用等效于Totin'Chip卡(http://en.wikipedia.org/wiki/Totin%27_Chip)才能使用结构。说真的
一个87.5万的人如何发布这样的答案?他小时候做过吗?
@Rohit-六年前;当时的现场标准大不相同。但是,这仍然是一个错误的答案,您是对的。

21> N_E..:

我刚刚处理Windows Communication Foundation [WCF]命名管道,我确实注意到使用Structs以确保数据交换是值类型而不是引用类型是有意义的.

推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有