.NET中的结构和类有什么区别?
当前回答
除了访问说明符的基本区别,以及上面提到的几个区别之外,我还想添加一些主要区别,包括上面提到的一些区别,以及带有输出的代码示例,这将提供对引用和值的更清晰的了解
结构:
是值类型,不需要堆分配。内存分配不同,存储在堆栈中适用于小型数据结构影响性能,当我们将值传递给方法时,我们会传递整个数据结构,所有数据都传递给堆栈。构造函数只返回结构值本身(通常在堆栈上的临时位置),然后根据需要复制该值每个变量都有自己的数据副本,其中一个变量的操作不可能影响另一个变量。不支持用户指定的继承,它们隐式继承自类型对象
类别:
参考类型值存储在堆中存储对动态分配对象的引用构造函数是用new运算符调用的,但它不会在堆上分配内存多个变量可以引用同一对象对一个变量的操作可能会影响另一个变量引用的对象
代码示例
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 Object的初始值为:10
内部结构方法Struct对象的内部方法值为:20
结构对象的方法调用值为:10
类对象的初始值为:10
内部类方法类对象的内部方法值为:20
类对象的方法调用值为:20
在这里,您可以清楚地看到按值调用和按引用调用之间的区别。
其他回答
在.NET中,结构和类声明区分引用类型和值类型。
传递引用类型时,实际存储的只有一个。访问实例的所有代码都在访问同一个实例。
传递值类型时,每个值类型都是副本。所有代码都在自己的副本上运行。
这可以用一个例子来说明:
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.
对于一个班级来说,这将是不同的
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.
类可以是空的-引用可以指向空。
结构是实际值-它们可以为空,但决不能为空。因此,结构总是有一个没有参数的默认构造函数——它们需要一个“起始值”。
我♥ 可视化,在这里我创建了一个来展示结构和类之间的基本区别。
以及文本表示,以防万一;)
+--------------------------------------------------+------+----------------------------------------------+
| Struct | | Class |
+--------------------------------------------------+------+----------------------------------------------+
| - 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. |
+--------------------------------------------------+------+----------------------------------------------+
有关更多信息,请查看以下内容:
类和结构(官方文档)。在类和结构之间选择(官方文档)。
在.NET中,有两类类型,引用类型和值类型。
结构是值类型,类是引用类型。
一般的区别是,引用类型存在于堆中,值类型存在于内联中,也就是说,在定义变量或字段的任何位置。
包含值类型的变量包含整个值类型值。对于结构,这意味着变量包含整个结构及其所有字段。
包含引用类型的变量包含指针,或对内存中实际值所在位置的引用。
首先,这有一个好处:
值类型始终包含值引用类型可以包含空引用,这意味着它们此刻根本不引用任何内容
在内部,引用类型被实现为指针,并且知道变量赋值是如何工作的,还有其他行为模式:
将值类型变量的内容复制到另一个变量中,将整个内容复制到新变量中,使这两个变量不同。换句话说,复制后,对其中一个的更改不会影响另一个将一个引用类型变量的内容复制到另一个变量中,就会复制引用,这意味着现在有两个引用指向实际数据的同一个存储位置。换句话说,在复制之后,更改一个引用中的数据似乎也会影响另一个引用,但这只是因为您实际上只是在两个地方查看相同的数据
当您声明变量或字段时,以下是两种类型的区别:
变量:值类型位于堆栈上,引用类型位于堆栈中,作为指向堆内存中实际内存所在位置的指针(请注意EricLipperts系列文章:堆栈是一个实现细节)类/结构字段:值类型完全位于类型内部,引用类型位于类型内部作为指向堆内存中实际内存所在位置的指针。
类中声明的事件通过锁(this)自动锁定其+=和-=访问,以使其线程安全(静态事件根据类的类型锁定)。结构中声明的事件没有自动锁定其+=和-=访问。结构的锁(this)无法工作,因为只能锁定引用类型表达式。创建结构实例不能导致垃圾收集(除非构造函数直接或间接创建引用类型实例),而创建引用类型的实例会导致垃圾收集。结构始终具有内置的公共默认构造函数。类DefaultConstructor{静态空隙Eg(){直接yes=新的直接();//始终编译正常InDirect可能=新InDirect();//如果构造函数存在且可访问,则编译//...}}这意味着结构总是可实例化的,而类可能不是,因为它的所有构造函数都可以是私有的。类不可实例化{private NonInstantiable()//确定{}}结构Direct{private Direct()//编译时错误{}}结构不能有析构函数。析构函数只是对象的重写。伪装完成,结构作为值类型,不受垃圾收集的影响。结构Direct{~Direct(){}//编译时错误}InDirect类{~InDirect(){}//编译正常}~Indirect()的CIL如下所示:.method家族hidebysig虚拟实例无效Finalize()cil已管理{// ...}//方法结束间接::Finalize结构是隐式密封的,类不是。结构不能是抽象的,类可以。结构不能在其构造函数中调用:base(),而没有显式基类的类可以。结构不能扩展另一个类,类可以。结构不能声明类可以声明的受保护成员(例如,字段、嵌套类型)。结构不能声明抽象函数成员,抽象类可以。结构不能声明虚函数成员,类可以。结构不能声明密封的函数成员,类可以。结构不能声明重写函数成员,类可以。此规则的一个例外是,结构可以重写System.Object的虚拟方法,即Equal()、GetHashCode()和ToString()。
有一个有趣的“类vs结构”难题案例——当您需要从方法返回几个结果时:选择要使用的结果。如果你知道ValueTuple的故事,你就知道添加ValueTuple(结构)是因为它应该比Tuple(类)更有效。但这在数字上意味着什么?两个测试:一个是具有2个字段的结构/类,另一个是有8个字段的类型/类(维度大于4-从处理器节拍的角度来看,类应该比结构更有效,但当然也应该考虑GC负载)。
P.S.另一个特定案例“stuct or class with collections”的基准是:https://stackoverflow.com/a/45276657/506147
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 |
代码测试:
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;
}
}
}
}