3、C#核心編程結構下,
本學習主要參考Andrew Troelsen的C#與.NET4高級程序設計,這小節主要述說以下幾個東西:
這一小節是上一小節的補充,主要涉及到一下的知識細節:
1、C#方法的各種細節
2、探討out、ref和params關鍵字以及可選參數和命名參數
3、方法重載。
4、C#操作數組類型的細節和了解System.Array類類型中包含的功能。
5、枚舉結構和結構類型。
6、值類型和引用類型的區別。
7、探討可空數據類型以及?和??操作符的作用。
方法和參數修飾符
和Main方法類似,自定義方法可以有或者沒有參數,也可以有或者沒有返回值。方法可以在類或結構的范圍內實現(還可以在接口類型中設置原型)、並且可以被各種關鍵字(如internal、virtual、public等)修飾以限制其行為。方法的基本格式如下:
class A
{
static int Add(int x,int y){return x+y;}
}
接下來總結一下C#的參數修飾符。
1.默認的參數傳遞行為:
參數傳入函數的默認行為是按值傳遞。簡單來說,如果沒有為參數標記參數相關的修飾符,數據的副本就會被傳入函數。數值數據屬於值類型,因此,如果在成員的作用域內改變參數的值,改變的就是調用者數據值的副本,調用者完全不會意識到這種改變。
2.out修飾符:
簡單來說,就是輸出參數。定義為帶有輸出參數的方法有義務在退出這個方法前,必須給參數賦一個恰當的值。如,下述方法返回x/y的和到ans中:
static void Add(int x, int y, out int ans)
{
ans=x+y;
}
out參數有個很有用的用途,調用者可以通過它使用一次方法返回多個返回值。並且,調用一個帶有輸出參數的方法也需要使用out修飾符。
3.ref修飾符:
說一下我的理解,ref關鍵字的用法,相當於是使得值類型擁有類似引用類型的功能,通常是改變他們的值。下面是引用參數和輸入參數的區別:
輸出參數不需要在他們被傳遞給方法之前初始化,因為方法在退出之前必須為輸出參數賦值。而引用參數必須在他們被傳遞給方法之前初始化,因為實在傳遞一個對已存在變量的引用。
4.params修飾符:
C#使用params關鍵字支持參數數組的使用。params關鍵字可以把可變數量的參數(相同類型)作為單個邏輯參數傳給方法。例如如下方法:
static double calculate(params double[] values)
{}
這個方法定義為一個帶有double類型參數數組,可以給它傳遞任意數量的double類型參數。例如:calculate(4.1,4.2,...)
說明:為避免歧義,C#要求方法只支持一個params參數,而且必須是參數列表的最後一個參數。
5.定義可選參數:
大體上就是說,我們可以創建一個包含賦值了默認值的參數。例如下面方法:
static void A(string a, string b="SB")
{}
其中第二個參數就是我們賦值了一個默認值的可選參數。
同樣,為了避免歧義,可選參數必須放在方法簽名的最後,將可選參數放到非可選參數前面會引發編譯錯誤。
6.使用命名參數調用方法:
同可選參數一樣,支持命名參數的主要原因也是為了簡化與COM的互操作。
命名參數允許我們在調用方法時以任意順序指定參數的值,因此我們可以使用冒號操作符通過名稱來指定參數而不必按位置傳遞參數。參數用法如下:例如定義一個傳遞ConsoleColor參數的B方法。void B(ConsoleColor aaa);那麼就可以這樣用這個方法:B(aaa:ConsoleColor.White);
如果定義了一個包含可選參數的方法,命名參數就非常有用,調用該方法的時候,我們直接給命名參數賦值就行了,不用再指定可選參數的值了。
7.成員重載:
和其他的面向對象語言一樣,C#允許方法重載。簡而言之,當我們定義了一組名字相同的成員是,如果他們的參數數量(或類型)不同,這樣的成員就叫做被重載的成員。
例如同樣名字的B方法,傳遞的參數一個int類型,一個是double類型,這也是允許的。B(int aa)和B(double aa)是兩個不同的方法。通過參數的類型不同來區分。
C#中的數組操作
數組是一組通過數字索引來訪問的數據項。更准確的說,數組是一組相同類型的數據點(int數組,string數組等)。數組定義類似下面:
int[] myIntArray;
用法如:int[] myIntArray=new int[3];
賦值如:int[0]=199;
1.C#數組初始化語法:
除了逐字填充數組外,還可以用數組初始化語法來填充數組,通過在花括號{}內指定每一個數組項來實現,用法如下:
string[] myStrArray = new string[] { "A", "B" };
2.隱式類型本地數組:
var同樣可以用來定義隱式類型本地數組,定義方法如下(注意,數組的元素類型必須一樣,所以即使是隱式類型數組,內裡元素的類型也要一致,因為編譯的時候會確定類型):
var myStrArray = new[] { "A", "B" };
3.定義object數組:
因為System.Object是.net類型系統中所有類型的最終基類,這樣的話,如果我們定義了一個object數組,它的子項就可以是任何東西。用法如下:
object[] obj = new object[] { 1, "A", 'a' };
4.使用多維數組:
多維數組主要有兩種,一種叫做矩形數組。一種叫做交錯數組。
矩形數組排列是以矩陣的形式,聲明如下:int[,]=new int[6,6];表示聲明了一個6*6的矩形數組。
交錯數組,也就是數組的數組。聲明如下:int[][] = new int[5][];聲明了一個具有五個不同數組的數組。
5.數組作為參數(和返回值)
只要我們創建了一個數組,就完全可以把它當做參數進行傳遞或者作為成員返回值接受。如下:
static string[] A()
{
string[] B = new string[]{"aaa"};
return B;
}
6.System.Array基類:
我們創建的每一個數組都從System.Array類獲得很多功能。使用這些公共成員,我們就能使用統一的對項模型來操作數組。(Clear(),Sort()等)例如,反轉一個數組,操作如下:
string[] A = new string[]{"A","B"};
反轉用法:Array.Reverse(A);
枚舉類型
在構建系統的時候,創建一組符號名來對應已知的數字值會很方便。枚舉可以幫助我們實現這個功能。默認情況下,枚舉值的存儲類型是System.Int32。定義如下:
enum E
{
sss=12,//表示從12開始
aaa,//真實值13
bb//真實值是14
}
1.控制枚舉的底層存儲:
枚舉值的存儲類型也可以進行改變,如果我們將枚舉值存儲類型設置為byte而不是int,那麼可以這麼寫:
enum E:byte
{
aaa=10,
B=1,
CC=100
}
注意,如果我們枚舉值超出了枚舉類型的范圍,那麼就會導致編譯錯誤。如上,如果讓枚舉值等於999就會引發編譯錯誤。
2.聲明枚舉變量:
因為枚舉只不過是用戶自定義的類型,我們可以把它們作為函數的返回值、方法參數、本地變量等。
3.System.Enum類型:
.net枚舉從該類獲得了很多功能,這個類定義了許多用來查詢和轉換某個枚舉的方法。例如Enum.GetUnderlyingType()方法,它用來返回用於保存枚舉類型值的數據類型。
4.動態獲取枚舉的名稱/值對:
所有的枚舉都支持ToString方法,它返回當前枚舉值的字符串名。而另一個方法GetValue則可以返回枚舉的值。
結構類型
同C語言中的結構一樣,結構不只是一組名稱值對,結構式可以包含許多數據字段和操作這些字段的成員的類型。
定義結構:
struct A
{
public int x;
public void Disp()
{
Console.WriteLine("x={0}",x);
}
}
創建結構變量:第一種可以直接以結構名定義,如A a;但是這種必須為結構中的每個公共字段賦值,否則就會出錯。另一種方法是用new關鍵字來創建結構變量,它會調用默認的構造函數,不接受任何輸入參數。A a = new A();
值類型和引用類型
C#的結構和數組、字符串以及枚舉全都派生自System.ValueType。而該來的作用是確保所有的派生類都分配在棧上而不是垃圾回收堆上。創建和銷毀分配再棧上的數據都很快,因為它的生命周期是由定義的作用域決定的,而分配在堆上的數據由.net拉基回收器監控,其生命周期的決定因素有很多。
從功能上說,該類的唯一目的是,重寫由System.Object定義的虛方法來使用基於值的而不是基於引用的語法。
由於值類型使用基於值的語法,結構的生命周期是可以預測的,當結構變量離開定義域的范圍時,它就會立即從內存中移除。
1.值類型、引用類型和賦值操作符:
值類型賦值(例如結構),在棧上會保留兩個副本,給其中一個操作改變值,不會影響另一個副本的值。
和棧中的值類型相比,當對引用類型(類類型)應用賦值操作符時,我們就是在內存中重定向引用變量的指向。他們相當於引用了托管堆中的同一個對象,當改變其中一個的值的時候,另一個也會改變。
2.包含引用類型的值類型:
默認情況下,當值類型包含其他引用類型時,賦值將生成一個引用的副本。這樣就有兩個獨立的結構,但每一個都包含指向內存中同一個對象的引用(淺復制)。如果想要執行一直‘深復制’也就是讓副本不受自己影響,即將內部引用的狀態完全復制到一個新對象中時,需要實現ICloneable接口。
3.按值傳遞引用類型:
大概就是將類作為參數傳遞給方法,並且在方法內部進行多次修改類的參數和重新賦值。例如:
class A
{
public int a;
public A(int aa)
{
a = aa;
}
}
方法B如下:
void B(A a)
{
a.a = 100;//表示改變類內部的值,起作用
a=new A(99);//表示給類重新賦值,不起作用
}
因為, 在這裡,傳遞的值其實是復制了調用者對象的引用。由於B方法與調用者指向同一個對象,所以改變對象的狀態數據是可能的,但是無法把引用重新賦值給一個新對象。
4.按引用傳遞引用類型:
參考上面的例子,如果有一個C方法,它是按照引用傳遞引用類型。如:
void C(ref A a)
{
a.a = 100;//表示改變類中a的值,起作用
a=new A(99);//表示給a實例分配了一個堆上新的對象,起作用
}
按引用傳遞引用類型時需要記住的黃金規則如下:
1,如果按引用傳遞引用類型,被調用者可能改變對象的狀態數據的值和所引用的對象。
2,如果按值傳遞引用類型,被調用者可能改變對象的狀態數據的值,但不能改變所引用的對象。
C#可空類型
現在我們應該記得所有數值數據類型(包括Boolean數據類型)都是值類型,按照規則,null用來建立一個空的對象引用,所以值類型永遠不可以被賦值為null。
為了定義一個可空的變量類型,應在底層數據類型中添加問號(?)作為後綴。注意,這種語法只對值類型是合法的。如果試圖創建一個可空的引用類型,包括字符串,都是遇到編譯錯誤。同非空變量一樣,局部可空變量必須賦一個初始值。
1.使用可空類型:
涉及到數據庫編程時,可空數據類型可能特別有用,因為一個數據表中的列可能有意是空的。定義如下:public int? a=null;
返回可空類型:
public int? geta()
{
return a;
}
2.??操作符:
關於可空類型需要知道的最後一點是,可以使用??操作符。在獲得的值實際上是null時,我們可以用這個操作符給一個可空類型賦值。
例如,當上述方法返回值為null的時候,可以給本地變量賦值為100:
int a = geta() ?? 100;
使用??操作符的好處是,它比傳統的if/else條件的寫法更加緊湊。不過也可以使用如下代碼確保如果值為空,則設置默認的100:
int a= geta();
if(!a.HasValue)
a = 100;
Console.WriteLine("a的值是:{0}",a);
小結:
本小結主要總結了可以用來構建自定義方法的C#關鍵字,默認情況下參數按值傳遞。然而,如果參數被標記為ref或者out,我們可以按引用進行傳遞。除此之外,我還總結了關於方法重載,以及有關數組、枚舉和結構如何在C#中定義和在.net類庫中表示的方法。最後我們俺就了值類型和引用類型細節,以及包括當作為參數傳入方法後,他們如何反應,和如何使用?以及??操作符來和可空數據類型進行交互。