.NET中的struct和class有什么区别?

What's the difference between struct and class in .NET?

.NET中的struct和class有什么区别?


NET中,有两类:类型,类型和值类型的引用。

结构是值类型和引用类型是类。

一般的是,一个是生活资料型差分对和一堆型的生命价值,这是内联的,无论是你的或是定义场变量。

一个变量的值的含A型和价值型的整个价值。一个结构,这意味着变量包含整个结构,与所有它的域。

一个变量的指针引用类型包含含A或A是关于在别的地方,在那里的记忆resides的实际价值。

这有一个好处:开始

  • 值类型总是包含a值
  • 引用类型可以包含a零参考,问他们问题,不要任何东西此刻

在一个实现,参考类型分,知道,知道,变量赋值和工场,有其他的行为模式:

  • 复制内容到另一个变量的值型变量a,整个内容拷贝到新的变量,使两个不同的。换句话说,改变后,复制一个,不会影响其他
  • 复制内容到另一个变量引用型变量a,拷贝参考,这意味着你现在有两个引用相同的实际数据存储在别的地方。换句话说,改变后,复制的数据,在一个参考的绝对不是想对其他的为好,但只因为你真的只是在看相同的数据的地方。

当你声明变量或机场,这里是知识的不同的类型:

  • 值类型变量:生活在栈,在栈参考型的生活作为一个指向堆内存中某处的记忆(虽然在实际生活lipperts软腭音系列:Eric是栈的实现细节。
  • 类/结构场:生活在价值型和生活型,标准型分型作为一个在内存中的堆在那里的实际记忆的生活。


每个短的摘要:

类:只读

  • CAN支持继承
  • 一时间参考(指针)
  • 参考可以是零
  • 有可能通过新的内存开销

唯一的结构:

  • 不支持继承
  • 值类型是
  • 自然价值是由(样的整数)
  • 不能有一个零的参考,(除非是空值)
  • 没有记忆的新实例的开销"除非"盒装

这两个类和结构:

  • 复合数据类型,通常是一个变量,用于包含有一些逻辑关系
  • 方法和事件中的CAN
  • CAN接口支持


在.NET中,结构和类声明区分引用类型和值类型。

当传递一个引用类型时,实际上只存储了一个。访问实例的所有代码都访问同一个实例。

当传递一个值类型时,每个值类型都是一个副本。所有代码都在自己的副本上工作。

这可以用一个示例来显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct MyStruct
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input)
{
   input.MyProperty ="new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty ="initial value" };

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still"initial value"
// - the method changed a new copy of the structure.

对于一个班来说,这是不同的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyClass
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input)
{
   input.MyProperty ="new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty ="initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now"new value"
// - the method changed the instance passed.

类不能为空-引用可以指向空值。

结构是实际值-它们可以为空,但不能为空。因此,结构总是有一个没有参数的默认构造函数-它们需要一个"起始值"。


除了其他答案中描述的所有差异之外:

  • 结构不能有显式无参数构造函数,而类可以
  • 结构不能有析构函数,而类可以
  • 结构不能从另一个结构或类继承,而类可以从另一个类继承。(结构和类都可以从接口实现。)
  • 如果你正在看一段解释所有差异的视频,你可以查看第29部分-C教程-C中类和结构之间的差异。


    从微软在类和结构之间的选择…

    As a rule of thumb, the majority of types in a framework should be
    classes. There are, however, some situations in which the
    characteristics of a value type make it more appropriate to use
    structs.

    ? CONSIDER a struct instead of a class:

    • If instances of the type are small and commonly short-lived or are commonly embedded in other objects.

    X AVOID a struct unless the type has all of the following
    characteristics:

    • It logically represents a single value, similar to primitive types (int, double, etc.).
    • It has an instance size under 16 bytes.
    • It is immutable. (cannot be changed)
    • It will not have to be boxed frequently.

    类的实例存储在托管堆上。所有"包含"实例的变量都只是对堆上实例的引用。将对象传递给方法将导致传递引用的副本,而不是对象本身。

    结构(从技术上讲,是值类型)存储在使用它们的任何地方,很像原始类型。运行时可以随时复制内容,而无需调用自定义的复制构造函数。将值类型传递给方法涉及复制整个值,同样也不调用任何可自定义代码。

    C++/CLI名称更好地区分了:"REF类"是第一个描述的类,"ValueC类"是第二个描述的类。C使用的关键字"class"和"struct"只是必须学习的东西。


    结构和类之间的区别:

    • 结构是值类型,而类是引用类型。
    • 结构存储在堆栈上,而类存储在堆。
    • 值类型将其值保存在声明它们的内存中,但是引用类型保存对对象内存的引用。
    • 值类型在作用域丢失后立即销毁,而引用类型仅在作用域丢失后销毁变量。这个对象随后被垃圾收集器销毁。
    • 将结构复制到另一个结构时,该结构的新副本对一个结构的get created modified不会影响其他结构。
    • 当您将一个类复制到另一个类中时,它只复制引用变量。
    • 两个引用变量都指向堆上的同一对象。更改一个变量将影响另一个引用变量。
    • 结构不能有析构函数,但类可以有析构函数。
    • 结构不能有显式的无参数构造函数,而类can结构不支持继承,但类支持继承。两个支持从接口继承。
    • 结构是密封类型。

    为了使其完整,在使用Equals方法时还有另一个不同之处,该方法由所有类和结构继承。

    假设我们有一个类和一个结构:

    1
    2
    3
    4
    5
    6
    class A{
      public int a, b;
    }
    struct B{
      public int a, b;
    }

    在主方法中,我们有4个对象。

    1
    2
    3
    4
    5
    6
    static void Main{
      A c1 = new A(), c2 = new A();
      c1.a = c1.b = c2.a = c2.b = 1;
      B s1 = new B(), s2 = new B();
      s1.a = s1.b = s2.a = s2.b = 1;
    }

    然后:

    1
    2
    3
    4
    s1.Equals(s2) // true
    s1.Equals(c1) // false
    c1.Equals(c2) // false
    c1 == c2 // false

    因此,结构适用于类似数字的对象,比如点(保存X和Y坐标)。班级也适合其他人。即使两个人有相同的名字、身高、体重……,他们仍然是两个人。


    结构和类

    这样的结构是一个A型,它是价值存储在堆栈,但A型和一级是一个参考存储在堆。

    a结构不支持继承和多态性,但这一类的支持。

    默认,所有公共类结构是经常,但默认是经常私人自然保护区。

    作为一个结构是一个值类型,我们可以指定一个结构T零对象,但它是一个类的实例。


    除此之外,还有一个基本的区别值得注意,那就是它是如何存储在内存中的。这可能会对阵列的性能产生重大影响。结构是值类型,因此它们将值存储在指向的内存区域中,类是引用类型,因此它们引用所指向内存区域中的类,实际值存储在其他位置。

    • 对于结构,内存在包含类中分配,用于存储数据。
    • 对于一个类,包含类将只包含指向另一个内存区域中新类的指针。

    对于数组也是这样,因此一个结构数组在内存中看起来像这样

    [struct][struct][struct][struct][struct][struct][struct][struct]

    其中作为类数组的类如下

    [pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

    您感兴趣的实际值实际上并不存储在数组中,而是存储在内存的其他地方。

    然而,对于大多数应用程序来说,这种差异实际上并不重要,在高性能代码中,这将影响内存中数据的位置,并对CPU缓存的性能产生很大影响。如果可以/应该使用结构,则使用类会大量增加CPU上的缓存未命中数。

    现代CPU做的最慢的事情是不处理数字,它从内存中提取数据,一级缓存命中比从RAM中读取数据快很多倍。


    对于初学者来说,结构是通过值而不是通过引用传递的。结构对于相对简单的数据结构很好,而从架构的角度来看,通过多态性和继承,类具有更大的灵活性。

    其他人可能会给你比我更详细的信息,但是当我想要的结构很简单时,我会使用结构。


  • 类中声明的事件通过锁(this)自动锁定其+=和-=访问权限,以使其具有线程安全性(静态事件锁定在类的类型上)。结构中声明的事件不具有自动锁定的+=和-=访问权限。结构的锁(this)不起作用,因为只能锁定引用类型表达式。

  • 创建结构实例不能导致垃圾收集(除非构造函数直接或间接创建引用类型实例),而创建引用类型实例可以导致垃圾收集。

  • 结构始终具有内置的公共默认构造函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }

    这意味着结构始终是可实例化的,而类可能不是,因为它的所有构造函数都可以是私有的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }

    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
  • 结构不能有析构函数。析构函数只是对象的重写。伪装终结,而作为值类型的结构不受垃圾收集的约束。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }

    And the CIL for ~Indirect() looks like this:

    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
  • 结构是隐式密封的,类不是。
    结构不能是抽象的,类不能。< BR>结构不能在其构造函数中调用:base(),而没有显式基类的类则不能。< BR>结构不能扩展另一个类,类不能。< BR>结构不能声明类可以声明的受保护成员(例如字段、嵌套类型)。< BR>结构不能声明抽象函数成员,抽象类不能。< BR>结构不能声明虚函数成员,类不能。< BR>结构不能声明密封的函数成员,类不能。< BR>结构不能声明重写函数成员,类不能。< BR>此规则的一个例外是,结构可以重写System.Object的虚拟方法,即Equals()、GetHashCode()和ToString()。< BR>


  • 除了访问说明符的基本区别和上面提到的很少的区别之外,我还想添加一些主要的区别,包括上面提到的很少的差别和带有输出的代码示例,这将使我们对引用和值有一个更清晰的了解。

    Structs:

    • 是值类型,不需要堆分配。
    • 内存分配不同,存储在堆栈中
    • 适用于小型数据结构
    • 影响性能,当我们将值传递给方法时,我们传递整个数据结构,所有的都传递给堆栈。
    • 构造函数只返回结构值本身(通常位于堆栈上的临时位置),然后根据需要复制该值。
    • 每个变量都有自己的数据副本,并且一个变量上的操作不可能影响另一个变量。
    • 不支持用户指定的继承,并且它们隐式继承自类型对象

    班级:

    • 引用类型值
    • 堆中存储
    • 存储对动态分配对象的引用
    • 使用新的运算符调用构造函数,但不会在堆上分配内存。
    • 多个变量可以引用同一对象
    • 一个变量上的操作可能会影响另一个变量引用的对象

    代码样本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
        static void Main(string[] args)
        {
            //Struct
            myStruct objStruct = new myStruct();
            objStruct.x = 10;
            Console.WriteLine("Initial value of Struct Object is:" + objStruct.x);
            Console.WriteLine();
            methodStruct(objStruct);
            Console.WriteLine();
            Console.WriteLine("After Method call value of Struct Object is:" + objStruct.x);
            Console.WriteLine();

            //Class
            myClass objClass = new myClass(10);
            Console.WriteLine("Initial value of Class Object is:" + objClass.x);
            Console.WriteLine();
            methodClass(objClass);
            Console.WriteLine();
            Console.WriteLine("After Method call value of Class Object is:" + objClass.x);
            Console.Read();
        }
        static void methodStruct(myStruct newStruct)
        {
            newStruct.x = 20;
            Console.WriteLine("Inside Struct Method");
            Console.WriteLine("Inside Method value of Struct Object is:" + newStruct.x);
        }
        static void methodClass(myClass newClass)
        {
            newClass.x = 20;
            Console.WriteLine("Inside Class Method");
            Console.WriteLine("Inside Method value of Class Object is:" + newClass.x);
        }
        public struct myStruct
        {
            public int x;
            public myStruct(int xCons)
            {
                this.x = xCons;
            }
        }
        public class myClass
        {
            public int x;
            public myClass(int xCons)
            {
                this.x = xCons;
            }
        }

    产量

    struct对象的初始值为:10

    内部结构方法struct对象的内部方法值为:20

    结构对象的方法调用后值为:10

    类对象的初始值为:10

    内部类方法类对象的内部方法值为:20

    类对象的after方法调用值为:20

    在这里,您可以清楚地看到按值调用和按引用调用之间的区别。


    如前所述:类是引用类型,而结构是具有所有结果的值类型。

    作为规则框架的一个经验,设计指南建议在以下情况下使用结构而不是类:

    • 它的实例大小小于16个字节
    • 它逻辑上表示单个值,类似于基元类型(int、double等)。
    • 它是不可变的。
    • 它不必经常装箱

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    +-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
    |                       |                                                Struct                                                |                                               Class                                               |
    +-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
    | Type                  | Value-type                                                                                           | Reference-type                                                                                    |
    | Where                 | On stack / Inline in containing type                                                                 | On Heap                                                                                           |
    | Deallocation          | Stack unwinds / containing type gets deallocated                                                     | Garbage Collected                                                                                 |
    | Arrays                | Inline, elements are the actual instances of the value type                                          | Out of line, elements are just references to instances of the reference type residing on the heap |
    | Aldel Cost            | Cheap allocation-deallocation                                                                        | Expensive allocation-deallocation                                                                 |
    | Memory usage          | Boxed when cast to a reference type or one of the interfaces they implement,                         | No boxing-unboxing                                                                                |
    |                       | Unboxed when cast back to value type                                                                 |                                                                                                   |
    |                       | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |                                                                                                   |
    | Assignments           | Copy entire data                                                                                     | Copy the reference                                                                                |
    | Change to an instance | Does not affect any of its copies                                                                    | Affect all references pointing to the instance                                                    |
    | Mutability            | Should be immutable                                                                                  | Mutable                                                                                           |
    | Population            | In some situations                                                                                   | Majority of types in a framework should be classes                                                |
    | Lifetime              | Short-lived                                                                                          | Long-lived                                                                                        |
    | Destructor            | Cannot have                                                                                          | Can have                                                                                          |
    | Inheritance           | Only from an interface                                                                               | Full support                                                                                      |
    | Polymorphism          | No                                                                                                   | Yes                                                                                               |
    | Sealed                | Yes                                                                                                  | When have sealed keyword                                                                          |
    | Constructor           | Can not have explicit parameterless constructors                                                     | Any constructor                                                                                   |
    | Null-assignments      | When marked with nullable question mark                                                              | Yes (+ When marked with nullable question mark in C# 8+)                                          |
    | Abstract              | No                                                                                                   | When have abstract keyword                                                                        |
    | Access Modifiers      | public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
    +-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+


    Structs are the actual value - they can be empty but never null

    这是正确的,但是也要注意,对于.NET 2结构,支持可以为空的版本,并且C提供了一些语法上的优势,使其更易于使用。

    1
    2
    int? value = null;
    value  = 1;


    有一个有趣的例子是"类与结构"的拼图——当您需要从方法返回几个结果时的情况:选择要使用的结果。如果您知道valuetuple的故事——您知道添加了valuetuple(struct),因为它比tuple(class)更有效。但它在数字上意味着什么呢?两个测试:一个是具有2个字段的结构/类,另一个是具有8个字段的结构/类(维度大于4-就处理器标记而言,类应该比结构更有效,但当然还应该考虑GC加载)。

    另一个针对特定案例"sturct or class with collections"的基准是:https://stackoverflow.com/a/45276657/506147

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
    Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
    Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
    .NET Core SDK=2.0.3
      [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
      Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
      Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


                Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
    ------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
      TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
       TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
     TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
      TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
      TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
       TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
     TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
      TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

    代码测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    using System;
    using System.Text;
    using System.Collections.Generic;
    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Attributes.Columns;
    using BenchmarkDotNet.Attributes.Exporters;
    using BenchmarkDotNet.Attributes.Jobs;
    using DashboardCode.Routines.Json;

    namespace Benchmark
    {
        //[Config(typeof(MyManualConfig))]
        [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
        [ClrJob, CoreJob]
        [HtmlExporter, MarkdownExporter]
        [MemoryDiagnoser]
        public class BenchmarkStructOrClass
        {
            static TestStruct testStruct = new TestStruct();
            static TestClass testClass = new TestClass();
            static TestStruct8 testStruct8 = new TestStruct8();
            static TestClass8 testClass8 = new TestClass8();
            [Benchmark]
            public void TestStructReturn()
            {
                testStruct.TestMethod();
            }

            [Benchmark]
            public void TestClassReturn()
            {
                testClass.TestMethod();
            }


            [Benchmark]
            public void TestStructReturn8()
            {
                testStruct8.TestMethod();
            }

            [Benchmark]
            public void TestClassReturn8()
            {
                testClass8.TestMethod();
            }

            public class TestStruct
            {
                public int Number = 5;
                public struct StructType<T>
                {
                    public T Instance;
                    public List<string> List;
                }

                public int TestMethod()
                {
                    var s = Method1(1);
                    return s.Instance;
                }

                private StructType<int> Method1(int i)
                {
                    return Method2(++i);
                }

                private StructType<int> Method2(int i)
                {
                    return Method3(++i);
                }

                private StructType<int> Method3(int i)
                {
                    return Method4(++i);
                }

                private StructType<int> Method4(int i)
                {
                    var x = new StructType<int>();
                    x.List = new List<string>();
                    x.Instance = ++i;
                    return x;
                }
            }

            public class TestClass
            {
                public int Number = 5;
                public class ClassType<T>
                {
                    public T Instance;
                    public List<string> List;
                }

                public int TestMethod()
                {
                    var s = Method1(1);
                    return s.Instance;
                }

                private ClassType<int> Method1(int i)
                {
                    return Method2(++i);
                }

                private ClassType<int> Method2(int i)
                {
                    return Method3(++i);
                }

                private ClassType<int> Method3(int i)
                {
                    return Method4(++i);
                }

                private ClassType<int> Method4(int i)
                {
                    var x = new ClassType<int>();
                    x.List = new List<string>();
                    x.Instance = ++i;
                    return x;
                }
            }

            public class TestStruct8
            {
                public int Number = 5;
                public struct StructType<T>
                {
                    public T Instance1;
                    public T Instance2;
                    public T Instance3;
                    public T Instance4;
                    public T Instance5;
                    public T Instance6;
                    public T Instance7;
                    public List<string> List;
                }

                public int TestMethod()
                {
                    var s = Method1(1);
                    return s.Instance1;
                }

                private StructType<int> Method1(int i)
                {
                    return Method2(++i);
                }

                private StructType<int> Method2(int i)
                {
                    return Method3(++i);
                }

                private StructType<int> Method3(int i)
                {
                    return Method4(++i);
                }

                private StructType<int> Method4(int i)
                {
                    var x = new StructType<int>();
                    x.List = new List<string>();
                    x.Instance1 = ++i;
                    return x;
                }
            }

            public class TestClass8
            {
                public int Number = 5;
                public class ClassType<T>
                {
                    public T Instance1;
                    public T Instance2;
                    public T Instance3;
                    public T Instance4;
                    public T Instance5;
                    public T Instance6;
                    public T Instance7;
                    public List<string> List;
                }

                public int TestMethod()
                {
                    var s = Method1(1);
                    return s.Instance1;
                }

                private ClassType<int> Method1(int i)
                {
                    return Method2(++i);
                }

                private ClassType<int> Method2(int i)
                {
                    return Method3(++i);
                }

                private ClassType<int> Method3(int i)
                {
                    return Method4(++i);
                }

                private ClassType<int> Method4(int i)
                {
                    var x = new ClassType<int>();
                    x.List = new List<string>();
                    x.Instance1 = ++i;
                    return x;
                }
            }
        }
    }

    基本值类型或结构类型的每个变量或字段都拥有该类型的唯一实例,包括其所有字段(公共和私有)。相比之下,引用类型的变量或字段可以为空,或者引用存储在其他地方的对象,其他引用也可能存在。结构的字段将与该结构类型的变量或字段存储在同一位置,该变量或字段可能位于堆栈上,也可能是另一个堆对象的一部分。

    创建基元值类型的变量或字段将使用默认值创建它;创建结构类型的变量或字段将创建新实例,以默认方式创建其中的所有字段。创建引用类型的新实例将首先以默认方式创建其中的所有字段,然后根据类型运行可选的附加代码。

    将一个基元类型的变量或字段复制到另一个将复制该值。将一个变量或结构类型的字段复制到另一个变量或结构类型的字段将前一个实例的所有字段(公共和私有)复制到后一个实例。将一个引用类型的变量或字段复制到另一个变量或字段将导致后者引用与前者相同的实例(如果有)。

    重要的是要注意,在某些语言中,比如C++,一个类型的语义行为与它的存储方式无关,但是.NET不是这样。如果一个类型实现可变值语义,那么将该类型的一个变量复制到另一个变量,将第一个变量的属性复制到第二个实例引用的另一个实例,并使用第二个实例的成员进行变异,这将导致第二个实例发生更改,但不会更改第一个实例。如果一个类型实现了可变的引用语义,那么将一个变量复制到另一个变量,并使用第二个变量的成员进行可变,则该对象将影响第一个变量引用的对象;具有不变语义的类型不允许突变,因此从语义上讲,复制是创建新实例还是创建另一个引用并不重要。首先。

    在.NET中,值类型可以实现上述任何语义,前提是它们的所有字段都可以这样做。但是,引用类型只能实现可变引用语义或不可变语义;具有可变引用类型字段的值类型仅限于实现可变引用语义或奇怪的混合语义。