c# 誤區系列(二)

前言

繼續整理誤區系列,可能會對剛入門的新手有些幫助,然後希望有錯誤的地方可以指出。

正文

關於泛型方法的確定

class Person<T>
{
   public void add(T a)
   {
   }
}

那麼請問這個add 是否是泛型方法。

初學者可能認為有泛型參數的就是泛型,其實不是的。

這個是為什麼呢?其實是這樣子的,當泛型類型確認的時候,那麼add 定義的時候就已經確定了類型。

比如說Person,那麼這個T就是string,Person 是泛型,但是Person不是。

當T確定是string的時候,在方法申明的時候就已經是string了,而不存在泛型這個概念。

所有泛型的開銷沒有我們想像的這麼大,在應用中,甚至使用泛型效率更高,不是說泛型是優化,而是泛型幫我們避免了太多裝箱和拆箱操作。

比如說以前的ArrayList,當我們把int 存進去,是裝箱,使用int又是拆箱。

泛型方法是這樣的:

class Person<T>
{
   public void add<Q>(T a,Q b)
   {
   }
}

在每次調用add的時候都必須確認Q的類型。

可空類型是引用類型?

因為可空類型是可以為空的,那麼初學者可能就認為可空類型是引用類型了。

其實可空類型是值類型,這個是為什麼?看下其中的原理。

說是可空值類型,裏面包含着一個判斷位。

這個是什麼意思呢,比如說一個位元組表示0-255,那麼會多分配一位去判斷這個位元組是否為空。

就是多一個位去判斷是否為空。

那麼這樣我們是不是就可以隨便使用呢?

從內存和cpu的角度來說,一個可空會增加一位,會增加內存消耗。同樣每次使用的時候都要判斷是否為null,會增加cpu負擔。

既然是值類型,那麼就存在裝箱和拆箱過程,那麼這個過程有什麼不同嗎?

裝箱時檢查是否為null,如果為null則直接返回null,如果不是null則獲取值進行裝箱。

拆箱時如果不是null,則返回值,否則返回null。

所以在c# 不能把null 看做是某個具體的地址,0x00之類的,更多的是一個概念。

那麼問題來了,為什麼int 不能為空?或者值類型不能為空?

很多回答是這樣子的,值變量的本身是具體的值。那麼難道引用類型不是指的具體的地址嗎?

個人覺得是這樣子的,int 類型的定義就規定了多少位為(應用程序如果判斷是int的),具有某種穩定的結構,如果破壞這種結構,那麼就不是int了。所以int類型不能為null,這是int類型的定義。

事件是一種特殊的委託?

個人認為這句話存在很大的問題,是一個概念性問題。

比如說,我們說正方形是一種特殊的長方形。

為什麼可以這麼說呢?來看一下長方形的定義。

長方形是有一個角是直角的平行四邊形。

長方形的性質為:兩條對角線相等;兩條對角線互相平分;兩組對邊分別平行;
兩組對邊分別相等;四個角都是直角;有2條對稱軸(正方形有4條);
具有不穩定性(易變形);長方形對角線長的平方為兩邊長平方的和;順次連接矩形各邊中點得到的四邊形是菱形。

從這個定義中,我們得知長方形包含了正方形,因為其中長方形並沒有定義長和寬不相等啊。

同樣正方形本身就是長方形,只是說正方形在長方形的條件下,增加了其他條件。

綜上所述,是可以這麼說的。

但是事件是一種特殊的委託,是真的不能這麼講,因為是兩種完全不同的概念。

什麼是事件?

1.事件的擁有者

2.事件成員(事件的本身)

3.事件響應者

4.事件處理器:本質上是一種回調方法

5.事件的訂閱:誰響應誰訂閱

什麼是委託?

委託是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞,這種將方法動態地賦給參數的做法,可以避免在程序中大量使用If-Else(Switch)語句,同時使得程序具有更好的可擴展性。

你會發現這是兩種是不同的概念。那麼是如何產生這種誤解的呢?

看到網上大量流傳着:public delegate void EventHandler(object sender, EventArgs e);

這只是說明委託是事件的一種驅動方式,如果把事件認為是一種委託就比較狹隘了,因為有些業務用到事件,如果想到事件就想到委託,就會陷入到僵局中,這樣沒有去從新定義更複雜的事件。

可能這樣不好理解,舉一個例子,比如說觀察者模式,c# 中委託作為觀察者例子,但是觀察者和委託沒有任何直接關係,難道沒有委託,觀察者就不存在?

具體可見觀察者://www.cnblogs.com/aoximin/p/13726813.html

datetime 是引用類型?

初學者看到datetime 有方法就認為是引用類型,因為值類型都更加簡單,沒有那麼多可操作的方法,然而datetime的確是值類型。

這裡涉及到一個問題,那麼就是值類型的定義上,值類型的判斷不是說存在的位置,也不是說值類型沒有方法,而是指這種類型的值是否具有一個穩定的結構(大小等)。

那麼是否值類型比引用類型性能更好呢?

這個肯定不是的。值類型(棧上)的優點在於,不用垃圾回收,不會因為類型標識而產生開銷,也不用解引用。存儲在堆上的值類型,直接和對象一起回收。

這裡解釋一下,為什麼不用垃圾回收,因為如果int 類型不可引用,表示在運行系統中沒有其地址了。再舉個例子,就是我們磁盤清空了,格式化了,數據還在,只是在它的運行系統中不認為其存在有效數據。

所以說為什麼值類型在創建的時候要清空分配的地址,是在使用的時候抹除的。

引用類型的有點在於傳遞,因為引用類型不用複製整個地址塊,只需要複製堆上對象的指定位置,32位是4個位元組,64是8個位元組。

對象在c# 中默認傳遞是引用傳遞的

這個問題涉及於,這樣一個場景。

void doSomething(Student student)
{
}
doSomething(a);

那麼問題是student是如何賦值的過程?是將這個a對象賦值給他嗎?

這個問題就是student這個變量存在堆上還是棧上了?student的值本身是地址,而地址是固定的類型(32位4個位元組,64位8個位元組),其實是值類型。

傳遞過程是將a的值傳遞給student,之所以叫做引用類型,是他們的值指向的位置。

那麼問題來了:

void doSomething(Student student)
{
}
doSomething(null);

那麼student是否有值?也是有值的,指向就是null,在引用類型中它本身就是一個對象。

未完,續。