<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
類和結構實際上都是建立物件(範例)的模版,每個物件都包含資料,並提供了處理和存取資料的方法。
類定義了類的每個物件可以包含什麼資料和功能。
class PhoneCus { public const string DaySend = "Mon"; public int CusId; }
結構與類的區別是它們在記憶體中的儲存方式,存取方式和它們的一些特性(稍後詳細介紹它們的區別)。
較小的資料型別使用結構可提高效能,在語法上,比較類似,主要區別是使用關鍵字struct代替class來宣告結構。
struct PhoneCusStruct { public const string DaySend = "Mon"; public int CusId=; }
對於類和結構,都是用new來宣告範例:這個關鍵字建立物件並對其進行初始化。
PhoneCus myCus = new PhoneCus(); PhoneCusStruct myCus2 = new PhoneCusStruct();
上面的例子,類和結構的欄位值都預設0.
類中的資料和函數稱為類的成員(資料成員和函數成員)。
資料成員是包含類的資料————欄位,常數和事件的成員。資料成員可以是靜態資料。類成員總是範例成員,除非用static顯示宣告。
函數成員提供了操作類中資料的某些功能,包括方法,屬性,建構函式,終端子,運運算元以及索引。
*C#區分函數和方法。C#中函數包含上述提到的。
引數可以通過參照或值傳遞給方法。在變數通過參照傳遞給方法時,被呼叫的方法得到的就是這個變數,準確的説就是指向記憶體中變數的指標。所以在方法內對變數進行的任何改變在方法退出後仍然有效。
而如果變數通過值傳遞給方法,被呼叫的方法得到的是變數的一個相同副本,也就是說,在方法退出後,對變數的修改會丟失。
對於複雜的資料型別,按參照傳遞的效率更高,因為在按值傳遞時,必須複製大量的資料。
注意字串的行為方式有所不同,因為字串是不可變的,所以字串無法採用一般參照型別的行為方式。在方法呼叫中,對字串所做的改變都不會影響原始字串。
像上面所説,值型別通過值傳遞變數是預設的。但也可以迫使值引數通過參照傳遞給方法。為此要使用ref關鍵字。這樣該方法對變數所做的任何改變都會影響原始值。
static void SomeFunction(int[] ints,ref int i) { ints[0] = 100; i = 100; }
在呼叫該方法的時候,必須新增ref關鍵字。
SomeFunction(ints, ref i);
C#要求變數在被參照前必須用一個初始值進行初始化。但使用out關鍵字來初始化可以簡化C# 編譯器所堅持的輸入引數的初始化。
在方法的輸入引數前加上out字首時,傳遞給該方法的變數可以不初始化。而且該變數通過參照傳遞,所以在從被呼叫的方法中返回時,對應方法對該變數進行的任何改變都會保留下來。
在呼叫該方法時,仍需要使用out關鍵字:
static void SomeFunction(int[] ints,out int i) { ints[0] = 100; i = 100; } SomeFunction(ints, out i);
引數一般需要按定義的順序傳遞給方法。命名引數允許按任意順序傳遞。
string FullName(string firstName,string lastName) { renturn firstName+" " +lastName; }
呼叫方法:
FullName("John","Doe"); FullName(lastName:"Doe",firstName:"John");
引數也可以是可選的。必須為可選引數提供預設值。可選引數還必須是方法定義的最後一個引數。
void TestMethod(int notOption,int option = 10) { Console.WriteLine( notOption + option); }
C#支援方法的過載————方法的幾個版本有不同的簽名(方法名相同,但引數的個數和/或型別不同)。
class MathTest { public int Value; public int GetSquare() { return Value*Value; } public int GetSquare(int x) { return x*x; } }
過載方法在引數方面的一些限制:
兩個方法不能僅在返回型別上有區別;
兩個方法不能僅根據引數是宣告為ref還是out來區分。
在任何語言中,對於方法過載,如果呼叫了錯誤的過載方法,就有可能出現執行錯誤。(後面討論如何避免這些錯誤)。
屬性是一個方法或一對方法,在使用者端看來,它是一個欄位。
public string SomeProperty { get { return "value"; } set { //設定屬性值 } }
get存取器不帶任何引數,且必須返回屬性宣告的型別。也不應為set存取器指定任何顯示引數,編譯器會
假定它帶一個引數,器型別也許屬性相同,並表示為value.
private int age public int Age { get { return age; } set { age = valeu; } }
注意所用的命名約定,採用C#的區分大小寫模式,使用相同的名稱,但公有屬性採用大寫形式命名,如果存在一個等價的私有欄位,則採用小寫形式命名。
一些開發人員喜歡使用把下劃線作為字首的欄位名,如_age,這會為識別欄位提供極大的便利。
在屬性定義中省略set存取器,就會建立唯讀屬性。這樣使用者端程式碼只可以讀取該屬性的值,但不能設定值。
private int age public int Age { get { return age; } }
同樣在屬性定義中省略get存取器,就會建立只寫屬性。
C#允許給屬性的gei和set存取器設定不同的存取修飾符,所以屬性可以有公有的get存取器和受保護的set存取器。
在gey和set存取器中,必須有一個具有屬性的存取級別(公有)。
如果屬性的set和get存取器中沒有任何邏輯,就可以使用自動實現的屬性。這種屬性會自動實現後背成員變數。
public int Age { get; set; }
不需要宣告private int age;,編譯器會自動建立它。
使用自動實現的屬性,就不能在屬性設定中驗證屬性的有效性。但必須有兩個存取器,不能把屬性設定為唯讀或只寫。
public int Age { get;//報錯 } 但是,每個存取器的存取級別可以不同, public int Age { get; private set; }
宣告基本建構函式就是宣告一個與包含的類同名的方法,但該方法沒有返回值。
public class MyClass { public MyClass() { } // }
一般情況下,如果沒有提供任何建構函式,編譯器會在後臺建立一個預設的建構函式。這是一個基本的建構函式,它只能把所有的成員欄位初始化為標準的預設值。這通常就足夠了,否則需要編寫自己的建構函式。
建構函式的過載與其它方法的規則相同。可以為建構函式提供任意多的的過載,只要它們的簽名有明顯區別。
public class MyClass { public MyClass() { } public MyClass(int i ) { / / } // }
如果提供了帶引數的建構函式,編譯器就不會自動提供預設的建構函式。只有在沒有定義任何建構函式的時候,編譯器才會自動提供預設的建構函式。
public class MyNum { private int number; public MyNum(int number) { this.number =number; } }
一般使用this關鍵字區分成員欄位和同名的引數。
如果試圖使用無引數的建構函式範例化物件就會報錯:
MyNum num = new MyNum();//報錯
可以把建構函式定義為private或protected,這樣不相關的類就不能存取它們:
public class MyNum { private int number; private MyNum(int number) { this.number =number; } }
上述例子沒有為MyNum定義為任何公有或受保護的建構函式。這就使MyNum不能使用new運運算元在外部程式碼中範例化,但可以在MyNum類中編寫一個公有靜態屬性或方法,以範例化該類。
這在下面兩種情況下受有用的:
類僅用作某些靜態成員或屬性的容器,因此永遠不會範例化它。
希望類僅通過某個靜態成員函數來範例化。
C#可以給類編寫無引數的靜態建構函式。這種建構函式只執行一次,而前面的建構函式是範例建構函式,只要建立類的物件,就會
執行它。
class MyClass { static MyClass() { } }
編寫靜態建構函式的一個原因是,類有一些靜態欄位或屬性,需要在第一次使用類之前,從外部源中初始化這些靜態欄位和屬性。
.NET執行庫不能確保什麼時候執行靜態建構函式,所以不能把要求在某個特定時刻執行的程式碼放在靜態建構函式中。也不能預計不同類的靜態建構函式按照什麼順序執行。但是可以確保靜態建構函式最多執行一次,就在程式碼參照類之前呼叫它。
在C#中,通常在第一次呼叫類的任何成員之前執行靜態建構函式。
注意,靜態建構函式沒有存取修飾符,其它C#程式碼從來不呼叫它,但在載入類時,總是由.NET執行庫呼叫它,所以像public,private這樣的存取修飾符就沒有任何意義。出於同樣原因,靜態建構函式不能帶任何引數,一個類也只能有一個靜態建構函式。很顯然,靜態構造只能存取累的靜態成員,不能存取類的範例成員。
無引數的範例建構函式與靜態建構函式可以在同一個類中同時定義。雖然參數列相同,但這並不矛盾,因為在載入類的時候執行靜態建構函式,在建立範例時執行範例建構函式,所以何時執行哪個建構函式不會有衝突。
如果任何靜態欄位有預設值,就在呼叫靜態建構函式之前指定它們。
下面演示靜態建構函式的用法:
class MainEntryPoint { static void Main() { Console.WriteLine("UserPreference:BackColor is " + UserPreference.BackColor.ToString()); } } class UserPreference { public static readonly Color BackColor; static UserPreference() { BackColor = Color.Red; } private UserPreference() { } }
該靜態變數在靜態建構函式中進行初始化。
有時,在一個類中有幾個建構函式,這些建構函式包含一些共同的程式碼。
class Car { private string des; private int nWheels; public Car(string des,int nWheels) { this.des = des; this.nWheels = nWheels; } public Car(string des) { this.des = des; this.nWheels = 4; } }
這兩個建構函式初始化了相同的欄位,顯然最好把所有的程式碼放在一個地方。C#有一個特殊的語法,稱為建構函式初始化器,可以實現這個目的。
class Car { private string des; private int nWheels; public Car(string des,int nWheels) { this.des = des; this.nWheels = nWheels; } public Car(string des):this(des,4) { } }
這裡,this關鍵字僅呼叫引數最匹配的那個建構函式。建構函式初始化器在建構函式的函數體之前執行。
C#建構函式初始化器可以包含對同一個類的另一個建構函式的呼叫,也可以包含對直接基礎類別的建構函式的呼叫,使用同樣的語法,但應用base關鍵字代替this.初始化器中不能有多個呼叫。
常數是一個包含不能修改的值的變數。但常數不必滿足所有的要求。有時需要一些一些變數,其值不應改變,但在執行之前其值是未知的。C#為這種情形提供了另一種型別的變數:唯讀欄位(readonly)。
readonly關鍵字比const靈活得多,允許把一個欄位設定為常數,但可以執行一些計算,以確定它得初始值。
其規則是可以在建構函式中給唯讀欄位賦值,但不能在其它地方賦值。唯讀欄位還可以是一個範例欄位,類的每個範例可以有不同得值。
與const不同,如果要把唯讀欄位設定為靜態,就必須顯示得宣告它。
var關鍵字用於表示隱式型別化得變數。var和new關鍵字一起使用時,可以建立匿名型別。
匿名型別只是一個繼承自Object且沒有名稱的類。
var caption = new {FirstName = "John",LastName="Doe"};
這會生成一個包含FirstName,LastName屬性的物件。
建立另一個物件:
var doctor = new {FirstName = "James",LastName="Mc"};
caption和doctor的型別就相同,可以設定caption = doctor
如果設定的值來自於另一個物件,就可以簡化初始化器。
var doctor = new {caption.FirstName,caption.LastName};
這些物件的型別名未知。編譯器為型別“偽造”了一個名稱,但只有編譯器才能使用它。
如果僅需要一個小的資料結構,此時類提供的功能多餘我們需要的功能,由於效能原因,最好使用結構。
結構是值型別,它們儲存在棧中或儲存為內聯(inline)(如果它們是儲存在堆中的另一個物件的一部分),其生存期的限制與簡單的資料型別一樣。
結構實際上是把資料項組合在一起,有時大多數位段都宣告為public。嚴格來說,這與編寫.net程式碼的規則相反(欄位應總是私有的(除const欄位外),並由公有屬性封裝)。但是,對於簡單的結構,公有欄位是可以接受的程式設計方式。
雖然結構是值型別,但在語法上可以把它當作類來處理。
struct PhoneCusStruct { public const string DaySend = "Mon"; public int CusId=0; } PhoneCusStruct phoneCusStruct = new PhoneCusStruct(); phoneCusStruct.CusId=3;
因為結構是值型別,所以new運運算元與類和其它參照型別的工作方式不同。new運運算元並不分配堆中的記憶體,而只是呼叫相應的建構函式,根據傳送給它的引數,初始化所有的欄位。
對於結構編寫下面的程式碼是合法的:
PhoneCusStruct phoneCusStruct; phoneCusStruct.CusId=3;
結構遵循其它資料型別都遵循的規則:在使用前所有的元素都必須進行初始化。在結構上呼叫new運運算元,或者給所有的欄位分別賦值,結構就完全初始化了。
如果結構定義為類的成員欄位,在初始化包含的物件時,該結構會自動初始化為0.
結構是會影響效能的值型別,但根據使用結構的方式,這種影響可能是正面的,也可能是負面的。正面的影響是為結構分配記憶體時,速度很快,因為它們將內聯或儲存在棧中。在結構超出了作用域被刪除時,速度也很快,不需要等待垃圾回收。負面影響是,只要把結構作為引數來傳遞或者把一個結構賦予另一個結構,結構的內容就會被複制,而對於類只複製參照。這樣就會有效能損失,根據結構的大小,效能損失也不同。
注意,結構主要用於小的資料結構。當把結構作為引數傳遞給方法時,應把它作為ref引數傳遞,以避免效能損失(這樣只傳遞了結構在記憶體中的地址)。
結構不能從一個結構中繼承。唯一的例外是對應的結構(和其它型別一樣)最終派生於類System.Object。因此結構也可以存取Object的方法。
在結構中也可以重寫Object中的方法——如ToString()方法。
結構的繼承鏈是:每個結構派生於System.ValueType類,System.ValueType類有派生於System.Object。ValueType並沒有給Object新增任何成員,但提供了一些更適合結構的實現方法。
注意,不能為結構提供其它基礎類別。
為結構定義建構函式的方式與類的方式相同,但不允許定義無引數的建構函式。因為在一些罕見的情況下,.NET執行庫不能呼叫使用者提供的自定義無引數建構函式,因此Microsoft乾脆採用禁止在C#的結構內使用無引數的建構函式。
預設建構函式會隱式的把欄位初始化,即使提供了其它帶引數的建構函式,也會先呼叫它。提供欄位的初始值也不能繞過預設建構函式。下面程式碼會編譯錯誤:
struct PhoneCusStruct { public int CusId =0; }
如果PhoneCusStruct宣告為一個類,就不會報錯了。
另外,可以像類那樣為結構提供Close()或Dispose()方法。
在應用程式程式碼內範例化一個類或結構時,只要有程式碼參照這個物件,就會形成強參照。這意味著垃圾回收器不會清理這個物件使用的記憶體,一般而言這是好事,因為可能需要參照這個物件,但是如果這個物件很大,而且不經常存取。這個時候可以建立物件的弱參照。
弱參照允許建立和使用物件,但在垃圾回收器執行時,就會回收物件並釋放記憶體。由於存在潛在的Bug和效能問題,一般不會這麼做,但在特定情況下使用是合理的。
弱參照使用WeakReference類建立。因為物件可能在任意時刻被回收,所以參照該物件前必須確認它的存在。
class MainEntryPoint { static void Main() { // Instantiate a weak reference to MathTest object WeakReference mathReference = new WeakReference(new MathTest()); MathTest math; if(mathReference.IsAlive) { math = mathReference.Target as MathTest; math.Value = 30; Console.WriteLine( "Value field of math variable contains " + math.Value); Console.WriteLine("Square of 30 is " + math.GetSquare()); } else { Console.WriteLine("Reference is not available."); } GC.Collect(); if(mathReference.IsAlive) { math = mathReference.Target as MathTest; } else { Console.WriteLine("Reference is not available."); } } } // Define a class named MathTest on which we will call a method class MathTest { public int Value; public int GetSquare() { return Value*Value; } public static int GetSquareOf(int x) { return x*x; } public static double GetPi() { return 3.14159; } }
partial關鍵字允許把類,結構,介面放在多個檔案中。
partial關鍵字的用法:把partial放在class,struct,interface前面即可。
如果宣告類時使用了下面的關鍵字,這些關鍵字就必須應用於同一個類的所有部分:
public,private,protected,internal,abstract,sealed,new,一般約束
在把部分類編譯後,類的成員和繼承等會合並。
如果類只包含靜態的方法和屬性,該類就是靜態的。靜態類在功能上與使用私有靜態建構函式建立的類相同。都不能建立靜態類的範例。
使用static關鍵字,編譯器可以檢查使用者是否給該類新增了範例成員。如果是,就會生成一個編譯錯誤。這可以確保不建立靜態類的範例。
static class PhoneCusStruct { public static void GetPhene() { } }
呼叫:PhoneCusStruct.GetPhene();
前面提到,所有的.NET類都派生自System.Object類.實際上,如果在定義類的時候沒有指定基礎類別,編譯器就會自動假定這個類派生自Object類。其實際意義在於,除了自己定義的方法和屬性等外,還可以存取Object定義的許多公有的和受保護的成員方法。這些方法可用於自己定義的其它類中。
System.Object的方法:
如果有類的原始碼,繼承就可以給物件新增方法。但如果沒有原始碼,則可以使用擴充套件方法,它允許改變一個類,但不需要該類的原始碼。
擴充套件方法是靜態方法,它是類的一部分,但實際上沒有放在類的原始碼中。假定PhoneCusStruct類需要一個Add()方法,但不能修改原始碼,就可以建立一個靜態類,把Add()方法新增為一個靜態方法:
public static class PhoneExtension { public static void Add(this PhoneCusStruct phoneCusStruct,string phone) { // } }
注意擴充套件方法的第一個引數是要擴充套件的型別,它放在this關鍵字的後面。這告訴編譯器,這個方法是PhoneCusStruct型別的一部分。在這個例子中,PhoneCusStruct是要擴充套件的型別。在擴充套件方法中,可以存取所擴充套件型別的所有公有方法和屬性。
呼叫:
PhoneCusStruct p =new PhoneCusStruct(); p.Add();//即使方法是靜態方法,也需要使用實體方法的語法。
如果擴充套件方法與類中的某個方法同名,就不會呼叫擴充套件方法。類中已有的任何實體方法優先。
到此這篇關於C#類和結構的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45