区分C# 中的 Struct 和 Class
翻译自ManjulataYadav2019年6月2日的博文《DifferenceBetweenStructAndClassInC#》,补充了一些内容和示例。
结构体(struct)是类(class)的轻量级版本。结构体是值类型,可用于创建行为类似于内置类型的对象。
比较
结构体和类共享许多特性,但与类相比有以下局限性。
- 结构体不能有默认构造函数(无参构造函数)或析构函数,构造函数中必须给所有字段赋值。
publicstructCoords { publicdoublex; publicdoubley; publicCoords()//错误,不允许无参构造函数 { this.x=3; this.y=4; } publicCoords(doublex)//错误,构造函数中必须给所有字段赋值 { this.x=x; } publicCoords(doublex)//这个是正确的 { this.x=x; this.y=4; } publicCoords(doublex,doubley)//这个是正确的 { this.x=x; this.y=y; } }
- 结构体是值类型,在赋值时进行复制。
- 结构体是值类型,而类是引用类型。
- 结构体可以在不使用new操作符的情况下实例化。例如:
publicstructCoords { publicdoublex; publicdoubley; } staticvoidMain() { Coordsp; p.x=3; p.y=4; Console.WriteLine($"({p.x},{p.y})");//输出:(3,4) }
- 结构体不能继承于另一个结构体或者类,类也不能继承结构体。所有结构体都直接继承于抽象类System.ValueType,System.ValueType又继承于System.Object。
- 结构体不能是基类,因此,结构体不能是abstract的,且总是隐式密封的(sealed)。
- 不允许对结构体使用抽象(abstract)和密封(sealed)修饰符,也不允许对结构体成员使用protected或protectedinternal修饰符。
- 结构体中的函数成员不能是抽象的(abstract)或虚的(virtual),重写(override)修饰符只允许重写从System.ValueType继承的方法。
- 结构体中不允许实例属性或字段包含初始值设定项。但是,结构体允许静态属性或字段包含初始值设定项。例如:
publicstructCoords { publicdoublex=4;//错误,结构体中初始化器不允许实例字段设定初始值 publicstaticdoubley=5;//正确 publicstaticdoublez{get;set;}=6;//正确 }
- 结构体可以实现接口。
- 结构体可以用作nullabletype(即:Nullable
中的T),对其赋值null值,参考【Nullable Struct】
什么时候使用结构体或类?
要回答这个问题,我们应该很好地理解它们的差异。
序号 | 结构体(struct) | 类(class) |
---|---|---|
1 | 结构体是值类型,可以在栈(stack)上分配,也可以在包含类型中内联分配。 | 类是引用类型,在堆(heap)上分配并垃圾回收。 |
2 | 值类型的分配和释放通常比引用类型的分配和释放更节约成本。 | 大的引用类型的赋值比大的值类型的赋值成本更低。 |
3 | 在结构体中,每个变量都包含自己的数据副本(ref 和 out 参数变量除外),对一个变量的操作不会影响另一个变量。 | 在类中,两个变量可以包含同一对象的引用,对一个变量的任何操作都会影响另一个变量。 |
这样,结构体(struct)只能在确定以下情形时使用:
- 它在逻辑上表示单个值,比如基本类型(int,double,等等)。
- 它是不可变的(immutable)。
- 它不会频繁地装箱和拆箱。
在所有其他情形,应该将类型定义为类(class)。
结构体示例:
structLocation { publicintx,y; publicLocation(intx,inty) { this.x=x; this.y=y; } } staticvoidMain() { Locationa=newLocation(20,20); Locationb=a; a.x=100; Console.WriteLine(b.x); }
输出将是20。“b”的值是“a”的副本,因此“b”不受“a.x”更改的影响。但是在类中,输出将是100,因为变量“a”和“b”引用同一个对象。
以下为译者补充
结构体实例与类实例
结构体实例的内存在栈(stack)上进行分配,所占用的内存随声明它的类型或方法一起回收。这就是在赋值时要复制结构体的一个原因。相比之下,类实例的内存在堆(heap)上进行分配,当对类实例的所有引用都超出范围时,为该类实例分配的内存将由公共语言运行时自动回收(垃圾回收)。
结构体实例的值相等性
两个结构体实例的比较是基于值的比较,而类实例的比较则是对其引用的比较。
若要确定两个结构体实例中的实例字段是否具有相同的值,可使用ValueType.Equals方法。由于所有结构都隐式继承自System.ValueType,因此可以直接在其对象上调用该方法,如以下示例所示:
publicstructPerson { publicstringName; publicintAge; publicPerson(stringname,intage) { Name=name; Age=age; } } staticvoidMain() { Personp1=newPerson("技术译站",100); Personp2; p2.Name="技术译站"; p2.Age=100; if(p2.Equals(p1)) Console.WriteLine("p2和p1有相同的值。"); Console.ReadKey(); } //输出:p2和p1有相同的值。
System.ValueType是值类型的隐式基类,它的Equals使用反射实现,因为它必须能够确定任何结构体中有哪些字段。在创建自己的结构体时,重写Equals方法可以提供特定于你的类型的高效求等算法。
“基于值的相等”这一点和C#9.0中新增的记录(record)类型具有相似之处
作者:ManjulataYadav
译者:技术译民
出品:技术译站
链接:英文原文
以上就是区分C#中的Struct和Class的详细内容,更多关于C#中Struct和Class的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。