ang*_*son 1006

在.NET中,有两类类型,引用类型值类型.

结构是值类型,类是引用类型.

一般的区别是引用类型存在于堆上,并且值类型保持内联,即,无论何处定义了变量或字段.

包含值类型的变量包含整个值类型值.对于结构,这意味着变量包含整个结构及其所有字段.

包含引用类型的变量包含指针或对实际值所在的内存中其他位置的引用.

这有一个好处,首先:

  • 值类型始终包含值
  • 引用类型可以包含引用,这意味着它们目前根本不引用任何内容

在内部,引用类型被实现为指针,并且知道并且知道变量赋值如何工作,还有其他行为模式:

  • 值类型变量的内容复制到另一个变量中,将整个内容复制到新变量中,使两者不同.换句话说,在复制之后,对一个的更改不会影响另一个
  • 引用类型变量的内容复制到另一个变量中,复制引用,这意味着您现在有两个对实际数据的其他位置存储的引用.换句话说,在复制之后,更改一个引用中的数据也会影响另一个引用,但这只是因为你真的只是在同一个数据中查看两个地方

声明变量或字段时,这两种类型的区别如下:

  • 变量:值类型存在于堆栈中,引用类型作为指向堆内存中某个地方的指针存在于实际内存中(尽管请注意Eric Lipperts文章系列:堆栈是实现细节.)
  • class/struct-field:value类型完全位于类型内部,引用类型位于类型内部,作为指向实际内存所在的堆内存中某处的指针.

  • 为了完全完整,我应该提到Eric Lippert已经说过[堆栈是一个实现细节](http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack -is-an-implementation-detail.aspx),每当我提到上面的堆栈时,请记住Eric的帖子. (40认同)
  • 另一个关键的区别是用法.来自MSDN:"结构通常用于封装一小组相关变量,例如矩形的坐标.结构也可以包含构造函数,常量,字段,方法,属性,索引器,运算符,事件和嵌套类型,尽管如此几个成员是必需的,你应该考虑让你的类型成为一个类." (8认同)
  • C++结构和类中的@KorayTugay绝对等效,除了一件事 - 默认访问限制(类默认为private,struct为public) (8认同)
  • @KorayTugay不,不是。 (3认同)
  • @KorayTugay 就“值类型与引用类型”而言,不,C++ 在这方面并不等同于 C#。在 C++ 中,您可以在堆栈上创建类的_实例_(相对于对在堆上分配的实例的引用)。正如berkus所说,在C++中,类和结构是等价的,除了默认访问之外。两者都可以充当值类型,也可以通过指针或引用来引用。 (3认同)
  • 这对C ++也有效吗? (2认同)

Tho*_*att 192

每个简短摘要:

仅限课程:

  • 可以支持继承
  • 是引用(指针)类型
  • 引用可以为null
  • 每个新实例都有内存开销

仅限结构:

  • 不能支持继承
  • 是价值类型
  • 按值传递(如整数)
  • 不能有空引用(除非使用Nullable)
  • 每个新实例都没有内存开销 - 除非'盒装'

类和结构:

  • 复合数据类型通常用于包含一些具有某种逻辑关系的变量
  • 可以包含方法和事件
  • 可以支持接口

  • 这个答案的某些部分并不完全正确.类并不总是在堆上,并且结构不总是在堆栈上.当前的异常包括类的结构字段,匿名方法和lambda表达式中捕获的变量,迭代器块以及已经提到的盒装值.但堆栈与堆分配是一个实现细节,可能会有所变化.[Eric lippart在此讨论](http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx).我已经投票了,但如果你更新,我会很乐意删除它. (15认同)
  • @MarkAmery我对你的表达式"没有内存开销"有同样的初始反应,但我认为OP指的是`class`的实例是托管内存(由垃圾收集器处理),而实例`struct`不是. (3认同)
  • struct 不支持从其他结构/类继承,但您可以在结构上实现接口。 (2认同)
  • 您可能想澄清当声明结构*“每个新实例没有内存开销” *时的含义。我的第一个解释是,您声称-显然是荒谬的-结构使用零内存。然后,我想也许您是想说一个结构,与类不同,它所需要的内存恰好等于其成员字段总和,而不再需要。但是后来我用Google搜索了“ c#struct memory memory”,并发现了Hans Passant的[this answer](http://stackoverflow.com/a/33672124/1709587),说不,也不是。所以你是什么意思? (2认同)
  • “结构按值传递(如整数)”是错误的:所有变量都是按值传递的,也是引用类型。如果要通过引用传递变量,则必须使用“ref”关键字。http://jonskeet.uk/csharp/parameters.html#ref (2认同)
  • @MarcoStaffoli“按值复制”是[Eric Lippert 用于描述值类型语义](https://blogs.msdn.microsoft.com/ericlippert/2009/04/27/the-stack-is-an-实现细节部分一/),所以我认为托马斯在这里使用术语“按值传递”是有道理的。另一方面是当前文档[支持您对该术语的理解](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-reference-type-参数)。作为一个专业人士,我们未能为引用类型的传递方式提出良好的术语,这是一个遗憾。 (2认同)

Kei*_*ith 39

在.NET中,struct和class声明区分引用类型和值类型.

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

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

这可以通过一个例子来展示:

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.
Run Code Online (Sandbox Code Playgroud)

对于一个课程,这将是不同的

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.
Run Code Online (Sandbox Code Playgroud)

类可以是空的 - 引用可以指向null.

结构是实际值 - 它们可以为空但从不为空.出于这个原因,结构总是有一个没有参数的默认构造函数 - 它们需要一个'起始值'.

  • 我不知道你是否正确理解了我的意思,我真的赞成/接受你的答案(与上面的答案相反),因为你的答案有很好的例子(不仅是理论解释,与上面的答案相反,上面的答案只有理论解释而没有例子)。 (2认同)

小智 23

结构和类之间的区别:

  • 结构是值类型,类是引用类型.
  • 结构存储在堆栈中,类存储在堆上.
  • 值类型在声明它们的内存中保存它们的值,但引用类型包含对象内存的引用.
  • 在范围丢失后立即销毁的值类型,而引用类型仅在范围丢失后销毁变量.该对象稍后被垃圾收集器破坏.
  • 将struct复制到另一个struct时,会创建该struct的新副本,修改一个struct不会影响另一个struct的值.
  • 将类复制到另一个类时,它只复制引用变量.
  • 两个引用变量都指向堆上的同一个对象.更改为一个变量将影响另一个参考变量.
  • 结构不能有析构函数,但类可以有析构函数.
  • 结构不能有明确的无参数构造函数,而类结构可以不支持继承,但类可以.两者都支持从接口继承.
  • 结构是密封型的.

  • “结构存储在堆栈上,而类存储在堆上”这句话不一定正确。请参阅/sf/ask/247945841/ (2认同)

Sun*_*est 20

来自微软在类和结构之间选择 ......

根据经验,框架中的大多数类型应该是类.但是,在某些情况下,值类型的特征使得使用结构更合适.

考虑结构而不是类:

  • 如果该类型的实例很小并且通常是短暂的或通常嵌入在其他对象中.

除非类型具有以下所有特征,否则X 避免使用结构:

  • 它在逻辑上表示单个值,类似于原始类型(int,double等).
  • 它的实例大小小于16个字节.
  • 这是不可改变的.(不能改变)
  • 它不必经常装箱.


小智 19

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

  1. 结构不能有明确的无参数构造函数,而类可以
  2. 结构不能有析构函数,而类可以
  3. 结构不能从另一个结构或类继承,而类可以从另一个类继承.(结构和类都可以从接口实现.)

如果您正在阅读解释所有差异的视频,您可以查看第29部分 - C#教程 - C#中的类和结构之间的差异.

  • 比.net语言通常不允许结构定义无参数构造函数(是否允许它由语言编译器决定)这一事实更重要的是结构可以存在并暴露出来没有任何类型的构造函数被运行到外部世界(即使定义了无参数构造函数)..net语言通常禁止结构的无参数构造函数的原因是为了避免由于有时运行这种构造函数而有时不运行而导致的混淆. (3认同)

Ars*_*yan 17

一世 ?可视化,在这里我创建了一个来展示structsclasses之间的基本区别。 在此处输入图片说明


还有文字表示以防万一;)

+--------------------------------------------------+------+----------------------------------------------+
|                      Stack                       |      |                      Heap                    |
+--------------------------------------------------+------+----------------------------------------------+
| - 1 per Thread.                                  |      | - 1 per application.                         |
|                                                  |      |                                              |
| - Holds value types.                             |      | - Holds reference types.                     |
|                                                  |      |                                              |
| - Types in the stack are positioned              |      | - No type ordering (data is fragmented).     |
|   using the LIFO principle.                      |      |                                              |
|                                                  |      |                                              |
| - Can't have a default constructor and/or        |      | - Can have a default constructor             |
|   finalizer(destructor).                         |      |   and/or finalizer.                          |
|                                                  |      |                                              |
| - Can be created with or without a new operator. |      | - Can be created only with a new operator.   |
|                                                  |      |                                              |
| - Can't derive from the class or struct          |  VS  | - Can have only one base class and/or        |
|   but can derive from the multiple interfaces.   |      |   derive from multiple interfaces.           |
|                                                  |      |                                              |
| - The data members can't be protected.           |      | - Data members can be protected.             |
|                                                  |      |                                              |
| - Function members can't be                      |      | - Function members can be                    |
|   virtual or abstract.                           |      |   virtual or abstract.                       |
|                                                  |      |                                              |
| - Can't have a null value.                       |      | - Can have a null value.                     |
|                                                  |      |                                              |
| - During an assignment, the contents are         |      | - Assignment is happening                    |
|   copied from one variable to another.           |      |   by reference.                              |
+--------------------------------------------------+------+----------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请查看以下内容:


Zoo*_*oba 15

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

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

通过C++/CLI名称可以更好地区分:"ref class"是首先描述的类,"value class"是第二个描述的类.C#使用的关键字"class"和"struct"只是必须学习的东西.


小智 7

结构与类

结构是一种值类型,因此它存储在堆栈中,但类是引用类型并存储在堆上.

结构不支持继承和多态,但是类支持两者.

默认情况下,所有struct成员都是公共的,但类成员默认是私有的.

由于结构是值类型,因此我们不能将null分配给结构对象,但类不是这种情况.

  • 关于"所有结构成员都是公开的":如果我没有弄错,那是不正确的."默认情况下,类成员和结构成员的访问级别(包括嵌套类和结构)是私有的." https://msdn.microsoft.com/en-us/library/ms173121.aspx (5认同)

Wil*_*ood 7

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

  • 通过结构,可以在包含类中分配内存以存储数据。
  • 对于一个类,包含类将只在不同的内存区域中包含一个指向新类的指针。

数组也是如此,因此结构数组在内存中看起来像这样

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

作为一组类的数组看起来像这样

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

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

对于绝大多数应用程序而言,这种区别并不重要,但是,在高性能代码中,这将影响内存中数据的局部性,并对CPU缓存的性能产生重大影响。在可以/应该使用结构的情况下使用类将大大增加CPU上的高速缓存未命中数。

现代CPU最慢的操作不是处理数字,而是从内存中获取数据,并且一级缓存命中率比从RAM中读取数据快许多倍。


Nin*_*ing 7

为了使其完整,使用该Equals方法时存在另一个差异,该方法由所有类和结构继承.

让我们说我们有一个类和一个结构:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}
Run Code Online (Sandbox Code Playgroud)

在Main方法中,我们有4个对象.

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;
}
Run Code Online (Sandbox Code Playgroud)

然后:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false
Run Code Online (Sandbox Code Playgroud)

因此,结构适用于类似数字的对象,如点(保存x和y坐标).课程适合其他人.即使2个人有相同的名字,身高,体重......,他们仍然是2个人.


0xa*_*yan 7

+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                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                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)


Ed *_* S. 6

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

其他人可能会给您比我更多的细节,但是当我要使用的结构很简单时,我会使用结构。


Zai*_*Ali 5

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

  2. 创建结构体实例不会导致垃圾回收(除非构造函数直接或间接创建引用类型实例),而创建引用类型实例会导致垃圾回收。

  3. 结构体总是有一个内置的公共默认构造函数。

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 结构不能有析构函数。析构函数只是伪装的 object.Finalize 的覆盖,作为值类型的结构不受垃圾回收的影响。

    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
    
    Run Code Online (Sandbox Code Playgroud)
  5. 结构是隐式密封的,类不是。
    结构不能是抽象的,类可以。
    结构不能在其构造函数中调用 :base() 而没有显式基类的类可以。
    一个结构不能扩展另一个类,一个类可以。
    结构不能声明类可以的受保护成员(例如,字段、嵌套类型)。
    结构不能声明抽象函数成员,抽象类可以。
    结构不能声明虚函数成员,类可以。
    结构不能声明密封的函数成员,类可以。
    结构不能声明覆盖函数成员,类可以。
    此规则的一个例外是结构可以覆盖 System.Object 的虚拟方法,即 Equals()、GetHashCode() 和 ToString()。


小智 5

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

根据规则框架设计指南建议在以下情况下使用结构而不是类:

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