C#語法相比其它語言比較獨特的地方(二)
之前有個兄弟給我的卷一re了帖子,我當(dāng)時沒有g(shù),m,直到他把它刪掉才后悔莫及,人生最痛苦的事情莫過于此。。。。。。
好,即便如此,我們還是滿懷希望的向前奔去。接著寫卷二。
還要提一下,上次最后說到的delegate,在我了解了它的實現(xiàn)和用途以及看到我的偶像的一些訪問記錄后,我對它有了很深的理解,并且在事件處理機制上,我更偏向于我偶像這邊,有兄弟說MFC是switch,java是listener,其實java這些listener在processEvent里面還不是一個個switch然后調(diào)用,都是switch,那switch就不能說是特點了,最大的特點還是是否直接使用函數(shù)指針來進行響應(yīng)這點上。往下又說信號處理,以前的8259A發(fā)生中斷查中斷向量表進行到頭來也是地址轉(zhuǎn)移阿,這也沒特點,到后來調(diào)用都是地址轉(zhuǎn)移,而速度的差別就在于在同樣不可避免具有某些相同層次實現(xiàn)的情況下,機制不同架構(gòu)不同造成的效率差別,這就完全取決于實力了。
1,internal與protected,private
C#默認的,當(dāng)定義一個class的時候,如果你沒有加任何訪問修飾子,那么該class的訪問權(quán)限即為internal,當(dāng)然你可以顯式制定為internal。internal是什么呢?internal就是說在當(dāng)前工程中,都可訪問,不管你自己用了幾個名稱空間,都無所謂。
但是在定義一個class中的成員變量的時候,假如你什么都不寫,那么這個成員變量默認的權(quán)限就是private。如果你要這個成員在當(dāng)前工程中也可以被訪問,則必須使用internal關(guān)鍵字來顯式的修飾它。
另外,c#里面的protected訪問權(quán)限仍然和以前的c++中是一致的含義,表示只有繼承者才有訪問權(quán)限。即便是同一個工程,同一個名稱空間中的別的類,都別想看到這個protected成員,頗為嚴格的一個訪問限制。
internal和protected基本控制訪問是在不同的領(lǐng)域,他們兩個是可以同時用來修飾一個對象的。比如
class PPP
{
internal protected static int c=3;
}
沒有交集,也不會互斥。
這跟java是截然不同的,其實我本可以完全不提java,但由于我自己的背景和一個通盤概括,我還是把java的拿出來與c#進行類比。畢竟本文標題是“特別之處”,當(dāng)然,這里我也不知道你會認為是c#特別,還是java特別了。
java中沒有完全相同于這個internel似的訪問控制,它有一種獨特的package訪問控制。不管是類還是類的成員,如果你不寫訪問修飾,那么它就是package訪問級別的,package訪問級別的含義是在本package中都可以訪問。java中沒有那種類似internal的“在本工程”或者本“jar”中可以訪問的這種級別,只有package級別。
而java中的protected也是比c#中的protected要寬容得多,java中的protected的含義其實等價于c#中的protected并上java中的package。有人打了這么一個比方,說大宅門,有很多資源都是protected的,這些資源不但可以造福四鄰(同在一個package中),還可以給自己的兒子阿,孫子阿(兒子孫子通過繼承得到資源)即便他們遠走他鄉(xiāng)。
2,enum
我們學(xué)過C,C中定義enum中的元素符號的時候,這個符號不能夠與當(dāng)前作用域中的其他符號相同,并且,所有這些enum中的符號可以直接拿來當(dāng)常數(shù)使用,就好像是#define了一個整形常量一般。特別是當(dāng)不制定enum類型標記的時候,那簡直就是個#define。
C++中幾乎與C中相同,不同的是,當(dāng)定義一個這種enum對象的時候,不用
寫 enum 類型標記 對象;
而只用寫 類型標記 對象; 即可。
就好像以前必須寫 struct 類型標記 對象;
而在c++中就可以只用寫 類型標記 對象; 一樣但是不管是在c#或者是java中,首先它們都不可以省略類型標記。其次,不用確保必須不同于同個作用域的其他標記符。再者,不可以直接把enum中的元素符號直接拿過來當(dāng)常量。起碼也要寫,類型標記.元素符號 才行。
比如
enum XXX{A,B,C,D}
....
Console.writeLine(XXX.A);
但是C#中又有其特別之處,它保留了部分c的enum才有的功能。那就是可以用運算符+、-來對enum對象進行運算,這是java絕對做不到了,而且還可以轉(zhuǎn)換回int型別。比如
enum weekdays{sunday,monday.....saturday}
for(weekdays wd=weekdays.sunday;wd<=weekdays.saturday;wd++)
...
(int)weekdays.sunday //結(jié)果是0
也可以賦值
enum open_modes{
intput=1,output,append,
last_input_mode=input,last_output_mode=output
}
如果你覺得默認實現(xiàn)是int型太耗,可以用byte型
enum weekdays:byte{
....
}
3.string的==
我們都知道Equals方法可以給人覆蓋用來判斷兩個對象是否是同值,而==作用于兩個對象只能比較兩個對象的ref是否相等。
我們在java中比較兩個字符串是否相等用的是"hello".equals(aaa);
但是在c#中,string對象的==運算已經(jīng)被強行重寫,它就是表示equals,
這就是說,在c#中,實現(xiàn)字符串值比較的話,只需要寫成"hello"==aaa就行了,
這樣設(shè)計的目的是為了更直觀。
4.傳引用
在java中有個非常經(jīng)典的問題,這個問題的到訪真是讓我習(xí)以為常。
那就是字符串處理函數(shù)的問題。
在java或者c#中,有人
void processString(String a)
{
a=a+"asdfjsdf";
}
或者說是數(shù)字交換問題
void processInt(int a,int b)
{
int temp=a;
a=b;b=temp;
}
前者還有說的,后者則是連c的基礎(chǔ)都沒打好了,后者的話建議去補習(xí)C。
前者我來說一下,不管是java中還是c#中,string對象都是immutable的,即一經(jīng)產(chǎn)生,就無法改變,那么+運算符做了什么呢?它將生成一個新的string對象,然后把+兩邊的string的內(nèi)容都填進來。
也就是說a+"asdfdjkf"這個東西是一個全新的東西,如果寫成a+="asdfj"或者a=a+"sdfjk"那么原來的a和這個"asdjf"就可以被GC了。
再說java和c#中的對象型別,java和c#中所有的對象型別都是ref型別,也就是說。
String a;
這么一句話,只是定義了一個ref,它基本上不占用任何資源,也沒有生成任何真正的對象,它只是一個ref。
String a="dkfjsdf";的時候,在受控堆上生成一個對象"dkfjsdf",然后返回這個對象的ref給a。
我們再看剛才那個字符串處理,a只是一個類似局部變量的形式參數(shù),你將a的ref設(shè)為一個新值,然后函數(shù)返回,形式參數(shù)a沒了,原來的實際參數(shù)啥變化都沒有。
但是你說,我就是要這樣處理,這么辦呢?在java中,就沒法這樣處理String,不過StringBuffer之類的倒是可以,因為我們雖然無法改變實際參數(shù)的ref值,但是卻可以通過相同值ref更改對象內(nèi)部成員,對于immutable的我們沒辦法,但對于mutable的我們就可以捏了。
而在c#中,非常恭喜你可以得逞了。就像我們剛才設(shè)想的那樣去處理string是可以的,不過要這樣做。
void processString(ref string a)
{
a+="sdjkf";
}
加上了ref,就取消了形式參數(shù)的產(chǎn)生和壓退棧,就好像c++中的string &了,相當(dāng)于是直接將實際參數(shù)交給你了。
這樣我們對它為所欲為都是可以的,這樣我們的processString就得逞了。
不過在填實際參數(shù)的時候,需要寫成這樣
string h="haha";
processString(ref h);
Console.WriteLine(h);
我們就可以看到h被改變了。
5,out參數(shù)
out參數(shù)就好像直接通過c#語言實現(xiàn)了com接口定義中的out的語義一樣。
就是輸出參數(shù),我們知道不管是windows api還是com,函數(shù)返回值通常用來處理異常的,而真正處理的結(jié)果是通過輸出參數(shù)帶回的,輸出參數(shù)實現(xiàn)有很多種方式,比如傳地址,傳引用,當(dāng)然com中從來不用c++中詭異的那個&引用。
out參數(shù)跟我們之前提到的ref參數(shù)唯一不同的就在于,ref參數(shù)在填到實參之前,必須初始化,而out參數(shù)無此要求,它就是用來帶回結(jié)果的,你可以定義一個未初始化的局部變量,然后用out 變量名的寫法填進去,調(diào)用完畢,值就放在這個變量里了。
比如我們改改剛才的processString來說明out參數(shù)用法
void processString( out string a)
{
a="xxx";
}
string a;
processString(out a);
Console.WriteLine(a);
相關(guān)文章
C#獲取變更過的DataTable記錄的實現(xiàn)方法
這篇文章主要介紹了C#獲取變更過的DataTable記錄的實現(xiàn)方法,對初學(xué)者很有學(xué)習(xí)借鑒價值,需要的朋友可以參考下2014-08-08通過容器擴展屬性IExtenderProvider實現(xiàn)WinForm通用數(shù)據(jù)驗證組件
這篇文章介紹了通過容器擴展屬性IExtenderProvider實現(xiàn)WinForm通用數(shù)據(jù)驗證組件的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12詳解.NET 6如何實現(xiàn)獲取當(dāng)前登錄用戶信息
這篇文章主要介紹了.NET 6在應(yīng)用開發(fā)時是如何實現(xiàn)當(dāng)前登陸用戶信息獲取的,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下2022-01-01