1. 程式人生 > 其它 >Asp.Net Core 資料校驗(DataAnnotations,FluentValidation)

Asp.Net Core 資料校驗(DataAnnotations,FluentValidation)

疑問

  都知道C#有裝箱和拆箱的操作,聽聞也都是講int型別轉換成object型別就是裝箱,將object型別再轉回int型別就是拆箱。

  

  描述的通俗點:

  裝箱 將值型別轉換成引用型別,

  拆箱 將引用型別轉換成值型別。

  那看來是要先了解一下引用型別和值型別了。

引用型別和值型別

  在C#中,所有稱之為"類(class)"的型別,都是引用型別,而值型別都是標註為結構(struct)或者列舉(enum)。

  下面就來看一看引用型別和值型別,在例項化的時候發生了什麼操作(首先自然是申明一下兩種型別了):

  

  例項化了一個引用型別(SomeRef)和一個值型別(SomeVal),在呼叫的時候看看會發生什麼:

   

 

  

  上述程式碼執行完,可以看到在C#在操作引用型別的時候會執行以下幾步

    1.在託管堆上分配一塊記憶體;

    2.在分配的物件中,還需要加一些額外成員(型別物件指標,同步索引塊),這些成員必須初始化

    3.物件中的欄位要初始化為0,(本例中在執行someRef.x = 5時,將欄位x改為5)  

    4.在託管堆上分配一個物件時,可能會執行一次垃圾收集操作

 

  看完引用型別,接下來看一下值型別:

  

  

  可以看到相對於引用型別,值型別直觀的地方感覺就輕便了許多,
    1.線上程棧上分配一塊記憶體

    2.欄位就在物件本身(沒有額外的成員,也不包含指向例項的指標)

    3.值型別例項不受垃圾回收期控制,減少了應用程式在生存週期內進行垃圾回收的次數

  上面對值型別和引用型別做了個初步的解釋

  下面來看看什麼情況下會進行裝箱和拆箱

裝箱

  現在假設我們需要將一組座標點存到ArrayList集合中,以便後續的步驟使用,那麼我們會像下面這樣:

  1.定義一個表示座標的值型別,裡面分別有欄位x(橫座標),欄位y(縱座標)

  

  2.將幾個點存入ArrayList集合中

    

   通過上例可以看到,通過呼叫ArrayList的Add方法,將Point座標點新增到集合中

  

  這是ArrayList的Add方法原型,可以看到他的接受引數是一個object型別

  

  但是object型別是由"類(class)"來宣告的,前面說到宣告為"類(class)"的型別都是引用型別,但是我們的Point宣告為"結構(struct)"是一個值型別

  可以看出,傳入的值型別會轉變為引用型別。 

  C#中為了讓一個值型別轉換成一個引用型別,需要進行一次裝箱操作,下面看一下裝箱操作具體會發生哪些事情:

    1.在託管堆上分配記憶體

    2.值型別的所有欄位複製到新分配託管堆記憶體上

    3.返回物件的地址(這個地址是對一個物件的引用,值型別現在是一個引用型別)

    注:舊的point物件不變,值型別轉換成引用型別的本質是重新建立了一個已裝箱的Point物件(引用型別)

拆箱

  談完裝箱,再來談一談拆箱

  在上述的座標點集合物件中,想要獲取第一個點的資訊

  

  現在知道ArrayList存的都是物件的引用(或指標),那要做的就是獲取元素0中包含的引用(或指標),並將它放到Point物件的例項pFirst中

  來看看拆箱的過程,完成了那些事情

    1.獲取已裝箱Point物件中的欄位地址

    2.將這些欄位的值從託管堆中複製宣告的Point物件例項pFirst中(pFirst是值型別,線上程棧中分配記憶體)

再說一句

  目前在C#中,肯定不會再繼續用ArrayList來儲存一些物件的集合了,因為有了一組新的泛型集合,

  例如用List<T>

  

  在使用的就是就規定是什麼型別,在存取資料的時候,不需要再進行多餘的裝箱和拆箱操作

  但是在寫程式碼的時候還是會隱藏很多拆箱和裝箱的過程,注意儘量避免裝箱和拆箱的操作,如果不可避免,那就儘量減少裝箱和拆箱的操作,可以檢視下方簡單示例(無實際意義):