區(qū)分C# 中的 Struct 和 Class
翻譯自 Manju lata Yadav 2019年6月2日 的博文 《Difference Between Struct And Class In C#》,補(bǔ)充了一些內(nèi)容和示例。
結(jié)構(gòu)體(struct
)是類(lèi)(class
)的輕量級(jí)版本。結(jié)構(gòu)體是值類(lèi)型,可用于創(chuàng)建行為類(lèi)似于內(nèi)置類(lèi)型的對(duì)象。
比較
結(jié)構(gòu)體和類(lèi)共享許多特性,但與類(lèi)相比有以下局限性。
- 結(jié)構(gòu)體不能有默認(rèn)構(gòu)造函數(shù)(無(wú)參構(gòu)造函數(shù))或析構(gòu)函數(shù),構(gòu)造函數(shù)中必須給所有字段賦值。
public struct Coords { public double x; public double y; public Coords() //錯(cuò)誤,不允許無(wú)參構(gòu)造函數(shù) { this.x = 3; this.y = 4; } public Coords(double x) //錯(cuò)誤,構(gòu)造函數(shù)中必須給所有字段賦值 { this.x = x; } public Coords(double x) //這個(gè)是正確的 { this.x = x; this.y = 4; } public Coords(double x, double y) //這個(gè)是正確的 { this.x = x; this.y = y; } }
- 結(jié)構(gòu)體是值類(lèi)型,在賦值時(shí)進(jìn)行復(fù)制。
- 結(jié)構(gòu)體是值類(lèi)型,而類(lèi)是引用類(lèi)型。
- 結(jié)構(gòu)體可以在不使用
new
操作符的情況下實(shí)例化。 例如:
public struct Coords { public double x; public double y; } static void Main() { Coords p; p.x = 3; p.y = 4; Console.WriteLine($"({p.x}, {p.y})"); // 輸出: (3, 4) }
- 結(jié)構(gòu)體不能繼承于另一個(gè)結(jié)構(gòu)體或者類(lèi),類(lèi)也不能繼承結(jié)構(gòu)體。所有結(jié)構(gòu)體都直接繼承于抽象類(lèi) System.ValueType,
System.ValueType
又繼承于System.Object
。 - 結(jié)構(gòu)體不能是基類(lèi),因此,結(jié)構(gòu)體不能是
abstract
的,且總是隱式密封的(sealed
)。 - 不允許對(duì)結(jié)構(gòu)體使用抽象(
abstract
)和密封(sealed
)修飾符,也不允許對(duì)結(jié)構(gòu)體成員使用protected
或protected internal
修飾符。 - 結(jié)構(gòu)體中的函數(shù)成員不能是抽象的(
abstract
)或虛的(virtual
),重寫(xiě)(override
)修飾符只允許重寫(xiě)從System.ValueType
繼承的方法。 - 結(jié)構(gòu)體中不允許實(shí)例屬性或字段包含初始值設(shè)定項(xiàng)。但是,結(jié)構(gòu)體允許靜態(tài)屬性或字段包含初始值設(shè)定項(xiàng)。 例如:
public struct Coords { public double x = 4; //錯(cuò)誤, 結(jié)構(gòu)體中初始化器不允許實(shí)例字段設(shè)定初始值 public static double y = 5; // 正確 public static double z { get; set; } = 6; // 正確 }
- 結(jié)構(gòu)體可以實(shí)現(xiàn)接口。
- 結(jié)構(gòu)體可以用作
nullable type
(即:Nullable<T>
中的T
),對(duì)其賦值 null 值,參考【Nullable<T> Struct】
什么時(shí)候使用結(jié)構(gòu)體或類(lèi)?
要回答這個(gè)問(wèn)題,我們應(yīng)該很好地理解它們的差異。
序號(hào) | 結(jié)構(gòu)體(struct) | 類(lèi)(class) |
---|---|---|
1 | 結(jié)構(gòu)體是值類(lèi)型,可以在棧(stack)上分配,也可以在包含類(lèi)型中內(nèi)聯(lián)分配。 | 類(lèi)是引用類(lèi)型,在堆(heap)上分配并垃圾回收。 |
2 | 值類(lèi)型的分配和釋放通常比引用類(lèi)型的分配和釋放更節(jié)約成本。 | 大的引用類(lèi)型的賦值比大的值類(lèi)型的賦值成本更低。 |
3 | 在結(jié)構(gòu)體中,每個(gè)變量都包含自己的數(shù)據(jù)副本(ref 和 out 參數(shù)變量除外),對(duì)一個(gè)變量的操作不會(huì)影響另一個(gè)變量。 | 在類(lèi)中,兩個(gè)變量可以包含同一對(duì)象的引用,對(duì)一個(gè)變量的任何操作都會(huì)影響另一個(gè)變量。 |
這樣,結(jié)構(gòu)體(struct
)只能在確定以下情形時(shí)使用:
- 它在邏輯上表示單個(gè)值,比如基本類(lèi)型(
int
,double
,等等)。 - 它是不可變的(immutable)。
- 它不會(huì)頻繁地裝箱和拆箱。
在所有其他情形,應(yīng)該將類(lèi)型定義為類(lèi)(class
)。
結(jié)構(gòu)體示例:
struct Location { public int x, y; public Location(int x, int y) { this.x = x; this.y = y; } } static void Main() { Location a = new Location(20, 20); Location b = a; a.x = 100; Console.WriteLine(b.x); }
輸出將是 20?!癰” 的值是 “a” 的副本,因此 “b” 不受 “a.x” 更改的影響。但是在類(lèi)中,輸出將是 100,因?yàn)樽兞?“a” 和 “b” 引用同一個(gè)對(duì)象。
以下為譯者補(bǔ)充
結(jié)構(gòu)體實(shí)例與類(lèi)實(shí)例
結(jié)構(gòu)體實(shí)例的內(nèi)存在棧(stack)上進(jìn)行分配,所占用的內(nèi)存隨聲明它的類(lèi)型或方法一起回收。 這就是在賦值時(shí)要復(fù)制結(jié)構(gòu)體的一個(gè)原因。 相比之下,類(lèi)實(shí)例的內(nèi)存在堆(heap)上進(jìn)行分配,當(dāng)對(duì)類(lèi)實(shí)例的所有引用都超出范圍時(shí),為該類(lèi)實(shí)例分配的內(nèi)存將由公共語(yǔ)言運(yùn)行時(shí)自動(dòng)回收(垃圾回收)。
結(jié)構(gòu)體實(shí)例的值相等性
兩個(gè)結(jié)構(gòu)體實(shí)例的比較是基于值的比較,而類(lèi)實(shí)例的比較則是對(duì)其引用的比較。
若要確定兩個(gè)結(jié)構(gòu)體實(shí)例中的實(shí)例字段是否具有相同的值,可使用 ValueType.Equals 方法。 由于所有結(jié)構(gòu)都隱式繼承自 System.ValueType,因此可以直接在其對(duì)象上調(diào)用該方法,如以下示例所示:
public struct Person { public string Name; public int Age; public Person(string name, int age) { Name = name; Age = age; } } static void Main() { Person p1 = new Person("技術(shù)譯站", 100); Person p2; p2.Name = "技術(shù)譯站"; p2.Age = 100; if (p2.Equals(p1)) Console.WriteLine("p2 和 p1 有相同的值。"); Console.ReadKey(); } // 輸出: p2 和 p1 有相同的值。
System.ValueType 是值類(lèi)型的隱式基類(lèi), 它的 Equals 使用反射實(shí)現(xiàn),因?yàn)樗仨毮軌虼_定任何結(jié)構(gòu)體中有哪些字段。 在創(chuàng)建自己的結(jié)構(gòu)體時(shí),重寫(xiě) Equals 方法可以提供特定于你的類(lèi)型的高效求等算法。
“基于值的相等”這一點(diǎn)和 C# 9.0 中新增的記錄(record) 類(lèi)型具有相似之處
作者 : Manju lata Yadav
譯者 : 技術(shù)譯民
出品 : 技術(shù)譯站
鏈接 : 英文原文
以上就是區(qū)分C# 中的 Struct 和 Class的詳細(xì)內(nèi)容,更多關(guān)于C# 中 Struct 和 Class的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)簡(jiǎn)易的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)簡(jiǎn)易的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04基于C#實(shí)現(xiàn)網(wǎng)頁(yè)爬蟲(chóng)
這篇文章主要為大家詳細(xì)介紹了基于C#實(shí)現(xiàn)網(wǎng)頁(yè)爬蟲(chóng)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03Unity Shader實(shí)現(xiàn)水波紋效果
這篇文章主要為大家詳細(xì)介紹了Unity Shader實(shí)現(xiàn)水波紋效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05C#實(shí)現(xiàn)WPS文件轉(zhuǎn)PDF格式的方法示例
這篇文章主要介紹了C#實(shí)現(xiàn)WPS文件轉(zhuǎn)PDF格式的方法,涉及C#針對(duì)office組件的相關(guān)引用與操作技巧,需要的朋友可以參考下2017-11-11解析XPath語(yǔ)法之在C#中使用XPath的示例詳解
本篇文章是對(duì)在C#中使用XPath的示例進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C#集合根據(jù)對(duì)象的某個(gè)屬性進(jìn)行去重的代碼示例
當(dāng)根據(jù)對(duì)象的Name屬性進(jìn)行去重時(shí),你可以使用以下三種方法:使用Distinct方法和自定義比較器、使用LINQ的GroupBy方法,以及使用HashSet,下面給大家介紹C#集合根據(jù)對(duì)象的某個(gè)屬性進(jìn)行去重的代碼示例,感興趣的朋友一起看看吧2024-03-03CefSharp如何進(jìn)行頁(yè)面的縮放(Ctrl+滾輪)
CefSharp簡(jiǎn)單來(lái)說(shuō)就是一款.Net編寫(xiě)的瀏覽器包,本文主要介紹了CefSharp如何進(jìn)行頁(yè)面的縮放,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06asp.net core 使用 tensorflowjs實(shí)現(xiàn) face recognition的源代碼
tensorflowjs,在該項(xiàng)目中使用了ml5js這個(gè)封裝過(guò)的機(jī)器學(xué)習(xí)JavaScript類(lèi)庫(kù), 使用起來(lái)更簡(jiǎn)單,本文給大家分享asp.net core 使用 tensorflowjs實(shí)現(xiàn) face recognition的源代碼,需要的朋友參考下吧2021-06-06深入DropDownList用法的一些學(xué)習(xí)總結(jié)分析
本篇文章是對(duì)DropDownList的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06