標籤:變數聲明 虛擬碼 工作流程 例子 def 落地 tco null 原理
聲明
本文歡迎轉載,原文地址:http://www.cnblogs.com/DjlNet/p/7192354.html
前言
關於這本書(《深入理解C# 第三版》)的詳細情況以及好壞,自行搜尋即可,我就不囉裡囉嗦的,此文責在備份,意在記錄一下第二次閱讀當中發現原先囫圇吞棗之處,也為了記憶深刻吧。對這裡還有一本《Clr via C# 第四版》也準備二次閱讀,關於精度細讀章節,知乎傳送門( 趙姐夫的回答 ): https://www.zhihu.com/question/27283360。就在昨天(9月9號)北京和上海相繼展開了技術分享會(有同步直播),這裡要給北京的sqlserver演講的老哥點贊,講得不錯挺落地和實在的,還有上海的分享的【.Net 微服務實戰】,看了心裡挺有感觸的,並不是說從知識或者實踐的層面領悟到有多少有多少,而是理解和明白這玩意兒為何而生,培養一點大局觀和思維套路吧,畢竟在DDD、微服務大行其道的今天只有不斷學習,然後溫故而知新,可能才能知道它們想表達的含義吧!
這些知識你還記得麼嗎,
\(^∀^)メ
上面扯了一些口水話,其實在學習偏架構的知識時候,那麼必經之路便是對語言基礎的足夠掌握以及對上層架構的理解掌握,那麼今天再去扯架構和架構之前就來回顧一下關於語言的一些小知識,以下是在下記錄一些書中查漏補缺自個人備份記憶使用,其中可能也包含以下不常用或者小知識,大神大佬請自動忽略 ,哈哈。
誤區:對象在C#中預設是通過引用傳遞的
我這裡摘錄引用書中部分描敘:首先“引用傳遞”的正式定義相當複雜,要涉及左值和類似的電腦科學術語,但是最重要的一點是,假如以引用傳遞的方式來傳遞一個變數,那麼調用的方法可以通過更改其參數值,來改變調用者的變數值(說到這裡有木有想到 ref out 關鍵字,是的它們就可以達到引用傳遞的效果)。那麼預設情況,就是沒有顯示使用關鍵詞的情況,參考型別變數的值才是引用(類似: 0x12345678 這種),而不是對象本身,且不需要按引用來傳遞參數本身,就可以更改該參數引用的那個對象的內容。例如:下面的方法更改了相關對象StringBuilder的內容,但是調用者的運算式引用的仍然是之前的那個對象:
void AppendHello(StringBuilder builder){ builder.Append("Hello");}
調用這個方法時,參數值(對StringBuilder的一個引用)是以值傳遞的方式傳遞的。如果想在方法內部更改builder的變數值,如:builder=null;對調用者來說是看不見的。
理解:JIT編譯器如何處理泛型
對於所有的封閉類型,JIT的職責就是將泛型型別的IL轉換為本地代碼,我們以List作為例子,首先JIT為每個以實值型別作為類型實參的封閉類型都建立不同的代碼,理論上對於一些實值型別來說,代碼是可以共用的,但是JIT必須十分謹慎,不僅需要考慮空間大小的問題,還要考慮記憶體回收的問題,所以第一次建立獨享的代碼。那麼,所有使用參考型別(string、stream、stringbuilder等)作為類型實參的封閉類型都共用相同的本地代碼,之所以可以這樣做,是由於所有引用都具有相同的大小(32位CLR上是4個位元組,64位CLR是8個位元組,在任何一個特定的CLR中所有引用具有相同的大小)。關於泛型的新增API,GetGenericTypeDefinition作用於已構造的類型,擷取它的泛型型別定義和MarkGenericType作用於泛型型別的定義,返回一個已構造類型,諸如此類還有:GetGenericArguments、IsGenericTypeDefinition、IsGenericType、MarkGenericMethod、IsGenericMethod等等。
Nullable類型理解與使用null進行賦值和比較原理
注意Nullable是一個結構也就是實值型別,對於例如Nullable的變數來說,直接包含了一個bool和一個int成員,而不是其他對象的引用。關於null賦值比較,原理:C#編譯器允許使用null在比較和賦值時表示一個可空類型的空值。其中這樣來處理的原因,相信也是從語言層面讓語義更加符合自然邏輯,獲得和參考型別null的同樣體驗。那麼到底編譯器關於可空實值型別幫我們做了什麼呐,int? a=null; if(a==null){ .... },與null比較其實在編譯器產生IL代碼中,被轉換為調用a.HasValue,對a賦值為null,其實也是調用了Nullable的建構函式建立一個空值執行個體而已,注意直接調用a.Value在沒有真正的值提供時將會拋出異常。
注意匿名函數變數捕獲
劃重點: 1、捕獲的是變數本身,而不是建立委託執行個體時它的值 2、捕獲的變數的生存周期被延長了,至少和捕獲它的委託一樣長 3、多個委託可以捕獲同一個變數 4、在迴圈內部,同一個變數聲明實際上會引用不同的變數“執行個體”(這點在R#也會提示開發人員) 5、如果捕獲的變數不會發生改變,就不需要擔心 6、在C#5或者以上修正foreach的表達含義,但是for依然需要注意。
理解Yield的工作流程和成為奠定非同步Async/Await的設計基石
這裡直接引用書中的代碼加強記憶和再次熟悉下流程,如所示代碼: 關於代碼的執行流程那得自個兒看看,慢慢跟著執行流程,相信就能明白Yield在代碼執行中起到的作用,就貌似能在不同的方法之間跳躍,總的來說可以歸為幾點: 1、在第一次調用MoveNext之前,CreateEnumerable中的代碼不會被調用 2、所有工作在調用MoveNext時就完成了,擷取Current的值不會執行任何代碼 3、在yield return的位置,代碼會停止執行,方法暫時返回調用者方法,在下一次執行MoveNext時有繼續在下一行代碼繼續執行 4、在一個方法中的不同位置可以編寫多個yield return語句 5、代碼不會在最後的yield return處結束而是通過返回false的MoveNext調用來結束方法的執行 上面說了這麼多,在看到第3和4點的時候有沒有感覺到await的部分作用似曾相識,await也可以讓方法返回而且可以等在那裡等到結果拿到之後接著那個“斷點”繼續執行,而且在一個Async標記的非同步方法呼叫中可以,有多個await的拆包操作,所以在一定程度上面yield在思想和設計層面上,給後面的C#5的非同步埋下了鋪墊。從狀態機器的角度來看(這塊並不是很熟悉,所以說錯了,請斧正)在C#5中為迭代器塊而產生的狀態機器和那些非同步函數而產生的狀態機器之間的相似性是驚人的。非同步開發中兩個複雜的問題就是:1、處理狀態 2、在感興趣的事情發生之前進行有效暫停,迭代器使得這兩個問題得以完美的解決。 這裡提供一下yield實現非同步虛擬碼,也是書中的程式碼片段:(虛擬碼是基於CCR實現的,主要是明白它想傳遞表達的意思,即可代碼部分看看就好)
static IEnumerator<ITask> ComputeTotalStockVal.(str.user,str.pass){ string token=null; yield return Arbiter.Receive(false,AuthService.CcrCheck(user,pass), delegate(string t){ token=t; }); IEnumerable<Holding> stocks=null; IDictionary<string,decimal> rates=null; yield return Arbiter.JoindReceive(false, DbService.CcrGetStockHoldings(token), StockService.CcrGetRate(token), delegate(IEnumerable<Holding> s,IDictionary<string,decimal> r) { stocks=s; rates=r; }); OnRequestComplete(ComputeTotal(stocks,rates));}
大致對上面代碼做個解釋說明雖然是虛擬碼,CCR對我們的代碼進行了調用也就是調用MoveNext,第一個yield return才會執行,AuthService裡面的CcrCheck方法啟動一個非同步請求,CCR會等待直到它完成,然後提供給它的委託來處理拿到的結果也就是token,然後接著再次調用MoveNext,方法接著執行,JoindReceive並行啟動兩個非同步請求,在兩個請求都完成的情況下,利用後續的委託去處理結果,在這之後MoveNext再次調用,就完成了全部的請求處理了。
小總結
再回看一些基礎知識的時候,才發覺原來語言層面的設計不比一些高層架構的設計的重要性和觀賞性差(雖然沒什麼可比性,哈哈),好了,天不早了,後面應該會出續篇,等這個完了之後,應該會開始對一些大家關注的點出發,腳踏實地的從代碼、需求和設計層面考慮去寫一些可使用的程式碼片段或者程式設計,用作以後參考或者借鑒。最最後,好好給自己一記耳光,咋就做人真懶呐,看看離上次發文都多長時間了,送給自己的話:從現在起做一個不那麼懶的人吧!謹記!!!
【C# in depth 第三版】溫故而知新(1) (轉)