提问



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


我在这里遇到了这些规则:[80]



  • 结构应该代表单个结构
    值。

  • 结构应该有一个内存
    占地面积小于16个字节。

  • 之后不应更改结构
    创建



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

最佳参考


OP引用的源代码具有一定的可信性......但是微软呢?结构使用的立场是什么?我从微软那里寻求了一些额外的学习,这就是我发现的:[81]



  如果是实例,请考虑定义结构而不是类
  类型很小,通常是短暂的或通常嵌入
  其他对象。


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

  
  

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

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

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

  6.   
  7. 不必频繁装箱。

  8.   



Microsoft一直违反这些规则



好的,无论如何,#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
}


*参考资料[82]


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


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



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

  2. 速度

  3. Entry永远不会作为Dictionary类之外的参数传递。进一步的调查显示,为了满足IEnumerable的实现,Dictionary使用Enumerator结构,每次请求枚举器时它都会复制...有意义。

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



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


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



  1. 上述结构中没有任何内容声明readonly - 不可变

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

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



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


尽管有散列键,但字典很快部分是因为实例化结构比引用类型更快。在这里,我有Dictionary<int, int>存储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中找到。简而言之:[83]



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

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

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



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



  1. 结构必须安全使用

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

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



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

其它参考1


每当你不需要多态,想要值语义,并希望避免堆分配和相关的垃圾收集开销。然而,需要注意的是,结构(任意大)传递比类引用(通常是一台机器)更昂贵())因此,课程最终可能会更快。

其它参考2


我不同意原帖中的规定。这是我的规则:


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


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


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



  • 它们的行为与分配时的普通对象(引用类型)不同
    当作为参数传递时,可能导致意外行为;
    如果看到代码的人这样做,这尤其危险
    不知道他们正在处理一个结构。

  • 他们不能继承。

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


其它参考3


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


修改



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


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

其它参考4


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


使用这些数据很尴尬,因为数据可能超出了结构的大小,并且你不想一直复制胖值。但是,直接在数组中访问它不会复制结构 - 它在-place(与列表索引器形成对比,它会复制)。这意味着可以使用索引进行大量工作:


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


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


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


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


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

其它参考5


来自C#语言规范:[86]



   1.7结构

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

  
  结构对于具有的小型数据结构特别有用
  价值语义。复数,坐标系中的点,或
  字典中的键值对都是结构的好例子。该
  对于小型数据结构,可以使用结构而不是类
  应用程序的内存分配数量差异很大
  施行。例如,以下程序创建并初始化
  一个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的赋值会创建值的副本,并且此副本不受后续分配给a.x的影响。


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

其它参考6


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

其它参考7


这是一条基本规则。



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

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



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
}

其它参考8


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


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

其它参考9


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


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

其它参考10


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


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

其它参考11


除了运行时和其他各种用于PInvoke目的的值类型之外,您应该只在两个场景中使用值类型。



  1. 当您需要复制语义时。

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


其它参考12


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


何时使用struct over class,


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

其它参考13


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


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


也可以设计一个结构,使其表现得像一个值类型,它保存一个可变大小的集合(无论何时结构都会被复制),但实现这一目的的唯一方法是确保没有对象struct持有引用的东西将暴露给任何可能会改变它的东西。例如,一个可以有一个类似于数组的结构,它包含一个私有数组,其索引的put方法创建一个新的数组,其内容就是那个原来除了一个更改过的元素之外。不幸的是,使这些结构有效地执行可能有些困难。虽然有时结构语义可以很方便(例如,能够将类似数组的集合传递给例程,使用调用者和被调用者都知道外部代码不会修改集合,可能比要求调用者和被调用者防御性地复制他们给出的任何数据更好,类引用指向永远不会出现的对象的要求变异通常是一个非常严重的约束。

其它参考14


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


正如您在回复中所看到的,有一个创造性方法的日志来使用它们。因此,这些指南只需要这样,总是为了性能和效率。


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


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


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

其它参考15


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


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

其它参考16


我用BenchmarkDotNet做了一个小基准,以便更好地理解数字中的结构优势。我正在测试通过结构(或类)的数组(或列表)进行循环。创建这些数组或列表超出了基准范围 - 显然类更重将利用更多内存,并将涉及GC。[88]


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


附:关于通过调用堆栈传递struct/class的另一个基准是https://stackoverflow.com/a/47864451/506147


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<C1> testListClass = new List<C1>();
        List<S1> testListStruct = new List<S1>();
        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;
        }
    }

其它参考17


我的规则是


1,始终使用课程;


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

其它参考18


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


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

其它参考19


我认为良好的初步近似是从不。


我认为好的第二个近似值是永远不会。


如果你非常渴望性能,请考虑它们,但随后要测量。

其它参考20


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

其它参考21


Struct可用于提高垃圾收集性能。虽然您通常不必担心GC性能,但有些情况下它可能会成为杀手锏。就像低延迟应用程序中的大型缓存一样。请参阅此帖子中的示例:


http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/[91]

其它参考22


结构或值类型可用于以下场景 -



  1. 如果要阻止通过垃圾回收收集对象。

  2. 如果是简单类型且没有成员函数修改其实例字段

  3. 如果不需要从其他类型派生或派生到其他类型。



您可以在此链接[92]上了解有关值类型和值类型的更多信息。

其它参考23


简而言之,使用struct if:


1-您的对象属性/字段不需要更改。我的意思是你只想给他们一个初始值,然后阅读它们。


对象中的2-属性和字段是值类型,并且它们不是那么大。


如果是这种情况,你可以利用结构来获得更好的性能和优化的内存分配,因为它们只使用堆栈而不是堆栈和堆(在类中)

其它参考24


C#struct是类的轻量级替代品。它可以与类几乎相同,但是使用结构而不是类更昂贵。原因有点技术性,但总而言之,类的新实例放在堆,其中新实例化的结构放置在堆栈上。此外,您不处理对结构的引用,例如类,但是您直接使用结构实例。这也意味着当您将结构传递给函数时,它是按值而不是作为参考。在关于函数参数的章节中有更多关于这一点。


因此,当您希望表示更简单的数据结构时,您应该使用结构,特别是如果您知道将要实例化大量数据结构。 .NET框架中有很多示例,其中Microsoft使用了结构而不是类,例如Point,Rectangle和Color结构。

其它参考25


我很少使用结构来做事。但那只是我。这取决于我是否需要对象可以为空。


如其他答案中所述,我使用类来实现真实世界的对象。我也有结构的心态用于存储少量数据。

其它参考26


在我看来,struct没有强大的语义,可以让用户深入了解何时使用它。


它类似于一个类,但湖的大部分功能。它是一种降级版本的类。
有很多关于什么时候不使用它的说法,但很少说何时使用它。


IMO,没有理由为什么结构应该首先用OO语言实现。实际上原始类型不应该存在于纯粹的OO语言中,但我离题了。


它可能是一种优化内容的方法。一种拳击免费的东西,可以在一些呼叫网站上进行优化。


我的2分,我会说它违反了语言原则的KISS并且尽可能地避免它。

其它参考27


结构在很多方面类似于类/对象。结构可以包含函数,成员并且可以继承。但是C#中的结构仅用于数据保存。结构占用较少的RAM 而不是类,并且更容易收集垃圾收集器。但是当你在你的结构中使用函数时,编译器实际上采用与类/对象非常相似的结构,所以如果你需要带有函数的东西,那么使用class/object