程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#學習筆記之結構體,

C#學習筆記之結構體,

編輯:C#入門知識

C#學習筆記之結構體,


1.概述

結構是一種與類相似的數據類型,不過它較類更為輕量,一般適用於表示類似Point、Rectangle、Color的對象。基本上結構能辦到的類全都能辦到,但在某些情況下使用結構更為合適,後面會有提到。

結構具有以下特點:

  • 結構可以實現接口。
  • 結構可以聲明帶參數的構造函數。

  • 結構不能聲明默認構造函數(沒有參數的構造函數)或析構函數。                                 

  • 結構是值類型,而類是引用類型。

  • 實例化結構體時可以不使用new運算符。                           

  • 結構類型是不可抽象、隱式密封的,故不能使用abstract和sealed修飾符。                                                

  • 在結構中聲明字段時,字段無法被初始化,除非字段加上關鍵字const或static。

  • 一個結構不能從另一個結構或類繼承,而且不能作為一個類的基。所有結構都直接繼承自 System.ValueType,後者繼承自 System.Object。                          

  • 結構在賦值時進行復制。將結構賦值給新變量時,將復制所有數據,並且對新副本所做的任何修改不會更改原始副本的數據。 不過結構仍然可以使用ref和out參數引用的方式傳值給函數成員。另,在使用值類型的集合(如 Dictionary<string, myStruct>)時,請務必記住這一點。

2.結構體聲明

//結構體的聲明 public struct Rectangle { public double x,y; public static double z = 5;//結構體裡的字段只有加上static或者const關鍵字才能被初始化 //結構體裡的構造函數必須是帶參數的 public Rectangle(double x1, double y1) { x= x1; y= y1; Console.WriteLine(x.ToString()+" "+y.ToString()); } } class Prgram1 { static void Main(string[] args) { //結構體的初始化 Rectangle rect1; Rectangle rect2 = new Rectangle(); Rectangle rect3 = new Rectangle(5, 5); //輸出各個的值 Console.Write("rect1:"); Console.WriteLine("x = {0}, y = {1}", rect1.x, rect1.y); Console.Write("rect2:"); Console.WriteLine("x = {0}, y = {1}", rect2.x, rect2.y); Console.Write("rect3:"); Console.WriteLine("x = {0}, y = {1}", rect3.x, rect3.y); } } /* 輸出: rect1: x = 0, y = 0 rect2: x = 0, y = 0 rect3: x = 5, y = 5 */ 示例1

3.結構體賦值

由於結構體是值類型,故將一個結構體賦給新變量時,同時將復制它的所有數據,但是對新副本所做的修改不會影響原結構體。簡單的說就是賦值時只復制值不復制地址。示例如下:

class Prgram2 { static void Main(string[] args) { Rectangle rect = new Rectangle(10, 10); Rectangle rect1 = rect; rect1.width = 5; rect1.length = 5; Console.Write("rect:"); Console.WriteLine("x={0},y={1}",rect.length,rect.width); Console.Write("rect1:"); Console.WriteLine("x={0},y={1}", rect1.length, rect1.width); } } /* 輸出: rect: x = 10, y = 10 rect1: x = 5, y = 5 */ 示例2

但在這裡我有過小小的疑問,MSDN中在說到結構體的賦值時提到:“結構可用作可以為 null 的類型,因而可向其賦 null 值”,這句話有點饒,乍一看就想一個值類型怎麼能賦空值呢。後來仔細看了一下這句話提到的“可以為null的類型”,才發現它指的是Nullable結構即可空類型,這下明白了。對於值類型如果要賦空值,可以使用Nullable結構。用法就是Nullable<Rectangle>或者更方便的Rectangle?,關於可空類型園子裡講的很多,這裡就不多說了。

4.結構體的裝箱拆箱

大家都知道,裝箱就是值類型到引用類型轉變的過程,拆箱就是引用類型到值類型轉變的過程。對於結構體來說當然也是這樣,當一個結構體轉換成Object或者它所繼承的接口類型時,就是裝箱,反之則是拆箱。值得一提的是,當我們把結構體轉換後,再次改變結構體的值,轉換成的引用對象的值是不變的,因為轉變過後該引用對象就是不再是原來的值對象了。示例如下:

interface IPolygon { int EdgeNum { get; set; } } public struct Rectangle : IPolygon { private int edgenum; public int EdgeNum { get { return edgenum; } set { edgenum = value; } } } class Prgram3 { static void Main(string[] args) { Rectangle rect = new Rectangle(); rect.EdgeNum = 4; Console.WriteLine("rect.EdgeNum={0}", rect.EdgeNum);//4 //隱式裝箱為接口類型 IPolygon polygon = rect as IPolygon; polygon.EdgeNum = 8; Console.WriteLine("Changed interface."); Console.WriteLine("polygon.EdgeNum={0},rect.EdgeNum={1}",polygon.EdgeNum,rect.EdgeNum);//8,4 //改變結構體的值再輸出 rect.EdgeNum = 5; Console.WriteLine("Changed Struct."); Console.WriteLine("polygon.EdgeNum={0},rect.EdgeNum={1}", polygon.EdgeNum, rect.EdgeNum);//8,5 } } 示例3

5.結構體的構造函數

關於結構體的構造函數,有以下幾點需要知道:

當結構包含引用類型作為成員時,必須顯式調用該成員的默認構造函數,否則該成員將保持未賦值狀態且該結構不可用。

當我們聲明一個結構體時,編譯器會自動為其生成一個隱含的構造函數,該構造函數會將結構體中的每個字段初始化為默認值表中顯示的默認值,而這個構造函數是不允許被替換的,故結構體不能自定義無參的構造函數。

當結構體中含有私有成員或以其他方式設置的不可訪問成員時,這些成員只能在有參構造函數中進行初始化。並且當定義有參構造函數時,一定要完成所有字段的初始化,如果沒有完成所有字段的初始化,編譯時會發生錯誤。

結構體可以使用靜態的構造函數,其使用方法與類的靜態構造函數相類似。當結構體的第一個實例成員或靜態成員或顯示聲明的構造函數被調用前,靜態構造函數會被調用,且在整個過程中只被調用一次。

public struct Rectangle { public double x,y; public static int edgeNum; static Rectangle() { Console.WriteLine("The static constructor invoked"); } public void Test() { Console.WriteLine("The Test function invoked"); } public Rectangle(double x1,double y1) { x = x1; y = y1; } } class Prgram4 { static void Main(string[] args) { Rectangle.edgeNum = 4; Console.WriteLine("Rectangle.edgeNum = {0}", Rectangle.edgeNum); Rectangle rect = new Rectangle(5, 5); Console.WriteLine("rect.x={0},rect.y={0}", rect.x, rect.y); Rectangle rect1=new Rectangle (); rect1.Test(); Console.Read(); } } //上面三種都可以觸發靜態構造函數,但只會出現一次"The static constructor invoked" 示例4

6.結構體的多態和繼承

結構體並不像類那樣存在繼承,它是隱式密封的,它可以實現接口但不能指定基類,並且由於這個特性它的成員的聲明不能加protected和internal關鍵字。

結構體的成員不能是abstract或者virtual,因而voerride修飾符只適用於重寫從System.ValueType繼承的方法。

關於結構體為什麼是不可繼承的,下面這段話解釋的很好:

為什麼設計編程語言時將結構設計成無繼承性?­
其實類的繼承是有相當的成本的 ——由於繼承性,每個類需要用額外的數據空間來存儲“繼承圖”來表示類的傳承歷史,
通俗地說來就是我們人類的家族家譜,裡面存儲著我們的祖宗十八代,只有這樣我們才知道我們從哪裡來的,而家譜肯定是需要額外的空間來存放的。
大家不要覺得這個存放“繼承圖”的空間很小,如果我們的程序需要用10000個點(Point)來存放游戲中的人物形體數據的話,
在一個場景中又有N個人,這個內存開銷可不是小數目了。所以我們可以通過將點(Point)申明成 Struct而不是class來節約內存空間。

7.結構體的適用場合

結構體適合一些小的數據結構,如Point、Rectangle、Color等,如果它們使用類類型的話,為了引用每個對象,則需分配更多內存;這種情況下,使用結構可以節約資源。或者如果確定數據結構不會用到一些面向對象的特性,那麼結構體與類相比是個更好的選擇。

--------------------------------------------分----------------割--------------線---------------------------------------------------

本文算是梳理了C#中有關結構體的基礎知識,雖然寫了不少,可基本上沒有涉及到更深,像一些更底層的東西都沒有說到。而且由於技術有限難免有所疏漏錯誤,如果您看出來請不吝指出,謝謝。

本文參考學習自以下地址,謝謝原作者們的貢獻:

MSDN-使用結構

C#之結構

C# 結構體 struct

C# - 結構體轉換為接口引用時隱式裝箱的影響

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved