本文是一個菜鳥所寫,僅供自用,不喜勿噴
1.值類型和引用類型
1.1堆和棧
簡單的說值類型存放在堆棧上面,引用類型的數據存放在托管堆上面(它的引用地址卻存放在堆棧上面)!
棧:它是一個內存數組,是一個先進后出的數據結構!
棧的特征:數據只能從棧頂進,從棧頂出!
堆:它是一個內存區域,可以分配大塊區域存儲某類型的數據,與棧不同的是它里面的數據可以任意排序和移除!
下面是園子的一張圖,貼上來供大家參考啊!
問 題 | 值 類 型 | 引 用 類 型 |
---|---|---|
這個類型分配在哪里? | 分配在棧上 | 分配在托管堆上 |
變量是怎么表示的? | 值類型變量是局部復制 | 引用類型變量指向被分配得實例所占的內存 |
基類型是什么? | 必須繼承自System.ValueType | 可以繼承自除了System.ValueType以外的任何類型,只要那個類型不是sealed的 |
這個類型能作為其他類型的基類嗎? | 不能。值類型是密封的,不能被繼承 | 是的。如果這個類型不是密封的,它可以作為其他類型的基類 |
默認的參數傳遞是什么? | 變量是按值傳遞的(也就是,一個變量的副本被傳入被調用的函數) | 變量是按引用傳遞(例如,變量的地址傳入被調用的函數) |
這個類型能重寫System.Object.Finalize()嗎? | 不能。值類型不好放在堆上,因此不需要被終結。 | 可以間接地重寫 |
我可以為這個類型定義構造函數嗎? | 是的,但是默認的構造函數被保留(也就是自定義構造函數必須全部帶有參數) | 當然! |
這個類型的變量什么時候消亡? | 當它們越出定義的作用域時。 | 當托管堆被垃圾回收時。 |
1.2裝箱和拆箱
關於裝箱和拆箱是一個老生常談的話題,也有很多文章來分析它,如:1. 6個重要的.NET概念:棧,堆,值類型,引用類型,裝箱,拆箱 2. 值類型的裝箱與拆箱淺析 3. 深入C#內存管理來分析值類型&引用類型,裝箱&拆箱,堆棧幾個概念組合之間的區別
這類的文章真的多了,再總結就沒多大的意義了,看的時候多寫寫代碼,多想想,就會明白的!
2.接口,抽象類,封裝,繼承,多態
接口和抽象類這兩個概念還真不容易理解,有的時候理解一半,換一種方法考考你,你就會暈,到現在說實話我還沒完全懂,一直沒有把握它們的精髓,最近在看<<你必須知道的.NET>>,這是第二次看,收獲很多...
大家還是有時間多看看<<你必須知道的.NET>>,這本書可以說是很詳細的講解了OO思想,還有看看設計模式的書,多想多練,可以時間會長一點,不過總有一點我們會開竅的...
這種東西不是通過總結一下就能熟練運用的,不過你起碼要有一點面向對象的思想,要想有這種思想必須學習前輩留下的知識總結,這種才能理論結合實踐,才能深入的了解OO思想
推薦文章:細細品味C#——抽象、接口、委托、反射(感謝蝦皮老師啊...)
3.迭代器
主要是對foreach的深入理解,以及對兩個接口的深入剖析(包括它們的泛型結構):IEnumerable(可枚舉類型),IEnumertor(可枚舉數),文章入口:使用IEnumerable和IEnumerator接口,從yield關鍵字看IEnumerable和Collection的區別
4.泛型
泛型保證了類型安全,避免了裝箱和拆箱的操作,提高了性能,可復用性也得到了很大的提高,下面就來說說基本的泛型語法吧!
項目中對於泛型和委托的結合運用也很多見,很多人不是為了語法而學習,而是泛型的擴展性讓我們必須要知道它,把它實實在在的運用到項目中去,提高擴展性...
泛型語法不是很復雜,包括定義泛型類型,泛型方法,指定泛型約束,還有泛型約束包括只包括哪些類型等等,這些語法只要花些時間就能明白了,難的是一種思想,o(︶︿︶)o 我還很菜啊...
推薦文章:細細品味C#——泛型系列專題(蝦皮幫我們已經整理關於泛型的精彩文章,看完之后會有很多的收獲)
5.集合
5.1一般集合
.NET Frameword中關於集合的類存儲在System.Collections命名空間下,其實一開始學習的時候感覺集合這個東西很神秘,能動態增加,刪除,選擇數據(比數據好用多了),可是在學習之后,它的神秘感也隨之消息,因為集合的底層代碼跟數組有着密切聯系的,請看:學習之路二:關於集合和數組內在聯系的深入了解(里面也有個鏈接,可以點擊學習)!
下面是非泛型集合類之間的關系圖:
5.2泛型集合
自從.NET Framework引用泛型概念之后,它在C#編程方面掀起了一個泛型熱潮,泛型實在太好用了,不僅是類型安全,可擴展性,重要的是在性能方面有了顯著提高,這讓我們苦逼的程序猿看到了曙光,哈哈...
泛型集合類存儲在System.Collections.Generic以及System.Collections.ObjectModel命名空間下,下面是集合類之間的關系圖:
推薦文章:細細品味C#——泛型系列專題(有個pdf文件,下載下來回家慢慢看,同志們)
6.反射
反射這東西兩面性很極端,很多人說它的壞,也有很它在某些方面有着重大的作用,下圖是關於類型反射所需要用到的類之間的關系圖:
除了類型反射之外,還有一種是程序集的反射,功能比較強大,可是我對它的研究比較少,我就推薦幾篇好文章把(下面幾篇文章我也正在學習中)...
推薦文章 : 1..Net 中的反射(序章) - Part.1
2..Net 中的反射(查看基本類型信息) - Part.2
3..Net 中的反射(反射特性) - Part.3
4..Net中的反射(動態創建類型實例) - Part.4
7.特性(Attribute)
特性這個東西,在面向對象編程中有着非常重要的最用,在架構設計框架的時候,考慮使用特性的幾率會非常的大!
特性結合反射技術就可以實現依賴注入,以前看到公司一個項目在寫測試代碼的時候,總是給每個方法加上[RollBack]的特性,當方法結束后,所有數據庫的操作都將會回滾,我很費解,因為RollBack是自己定義的,怎么就一加上這個特性就自動完成回滾了!
下面就是完整的Rollback代碼,可是我在使用它的時候遇到一個問題 ,就是它只可以用於單元測試,我嘗試着把它用於一般的方法當中,可是一直沒有實現回滾功能,我感到很費解,有興趣的朋友可以幫我看看...

只能在單元測試里面進行調用:
1 [TestClass()] 2 public class ProgramTest : TestFixture //繼承這個類 3 { 48 [TestMethod()] 49 [RollBack()] //添加這個RollBack特性,就能實現回滾了 50 public void MyTestTest() 51 { 52 SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder 53 { 54 DataSource = @"LBDZ-20120514VC\SQLEXPRESS", 55 InitialCatalog = "My", 56 }; 57 connectionString.IntegratedSecurity = true; 58 59 using (SqlConnection conn = new SqlConnection(connectionString.ToString())) 60 { 61 conn.Open(); 62 SqlCommand cmd = conn.CreateCommand(); 63 cmd.CommandText = "INSERT INTO dbo.MyTable ( id) VALUES ( 6666 )"; 64 cmd.ExecuteNonQuery(); 65 Console.WriteLine("OK"); 66 } 68 Assert.IsTrue(true); 69 } 70 }
Question:這個RollBack我至今還沒有弄懂它怎么來實現的,如果那個園友能看懂的話,可以私信給我或留言給我,我會打心里感謝你的,可能會涉及到AOP和IOC的知識,希望大家幫幫我把,糾結了很長時間啦...
推薦文章: 1. 關於C# 中的Attribute 特性
8.委托和事件
其實把理解事件跟字段和屬性聯系起來,雖然這樣說可能會不嚴謹點,但是從一些大的方面講事件就是對委托的封裝,類似於屬性對字段的封裝,這種說法還是行得通的!
想要定義一個完整的委托和事件,需要經歷一下步驟(需要注意一些命名規范):
① 定義事件 → 委托使用微軟提供的EventHadler<TEventArgs>泛型委托,一般都會有兩個參數:
A) “object sender”定義的事件依附的對象,也就是事件定義在那個類中,那么這個參數就為這個類的實例化對象,一般都會用“This”!
B) “EventArgs e”也就是用於傳遞一些參數信息的對象,也可以使用自己定制的參數了
② 創建參數類 → 如果有必要定制的數據參數類(這個類似於創建自己的實體類用來傳遞信息),這個參數類應該繼承於EventArgs這個類!
③ 執行事件 → 其實在執行事件的時候還是有一定的規范的,比如方法名必須為“On+事件名”,還有在執行事件要判斷下時候為null,,然后在調用!
④ 注冊事件 → 調用事件(在傳遞事件對象的時候最好用“this”關鍵字)
⑤ 依附事件的方法 → 最后定義依附在這個事件中的方法,也就是執行這個事件的方法體,深入了解,其實依附事件中的方法其實都最終依附在事件衣服的委托中,這個委托會生成一個委托實例,以及一個委托鏈!
委托和事件定義語法:
委托: 訪問修飾符 + delegate + 返回值類型 + 委托名(參數列表);
事件: 訪問修飾符 + event + 委托名 + 事件名;
委托和事件跟觀察者模式聯系比較密切,可是我還是沒有理解它的精髓,可能是我還太菜了...
總結:靈活運用事件和委托將會給你的程序帶來更好的擴展性,這需要豐富經驗的積累,好了推薦幾篇我曾經學習過的文章把!
推薦文章:1. 庖丁解牛——深入解析委托和事件
2. C# 中的委托和事件
3. C#中的委托和事件(續)
9.線程
對於線程學習過,可是一直沒有做過多線程的項目,一直沒有領悟到它的精髓,也只能停留在表面的高度!
我就想說下Thread中的后台線程和前台線程(默認為“前台線程”),在這里總結下(其實我也是學習前輩們的知識)。
前台線程:當所有的前台的線程都執行完畢以后才會退出程序!
后台線程:對於后台線程,程序是不管你是否是執行完成的,不過當你程序一旦強制退出,后台線程也會終止的!
1 Thread thread = new Thread(delegate() 2 { 3 Console.WriteLine("線程開始工作"); 4 Thread.Sleep(2000); //暫停兩秒鍾 5 Console.WriteLine("線程結束"); 6 }); 7 thread.IsBackground = true; //分別設置為true和false,看看控制台運行的情況,我相信你能很快明白的 8 thread.Start(); 9 Console.WriteLine("主線程結束");
總結:設置為后台線程相當於我們說的異步,而前台線程就相當於同步,執行好線程在執行主程序!
能夠熟練使用多線程,還是要在項目中不斷的實踐,可是項目是可遇而不可求的東西,現在我的項目是肯定要不到了,只能自己看看文章,熟悉熟悉知識啊...
推薦文章: 1. C# 溫故而知新: 線程篇(一)
10.六種異步方式
10.1 委托異步模型
使用的是委托的BeginInvoke和EndInvoke異步執行模式!
必須要有兩個條件:
① 必須要有個委托作為寄宿體
② 執行函數 ExecuteFunction
③ 回調函數 CallBackFunction ,所謂的回調函數就是獲取執行函數的返回值!
有了上面三種條件之后,就可以直接調用Begin和End進行委托異步編程了,其中還有細節問題需要注意,下面我們就一一來看!
具體思路步驟:
① 選擇一個適合的委托類型,如參數列表,返回值類型
② 創建一個執行函數,必須跟委托的參數列表和返回值類型對應起來
③ 創建一個回調函數,它只有一個參數沒有返回值,參數類型為IAsyncResult類型,這是使用委托實現異步的規范寫法,不可改變
代碼實現:
1 //寫一段簡潔的代碼 2 private void button1_Click(object sender, EventArgs e) 3 { 4 //定義委托,並指定異步的執行方法 5 Func<string, string> func = new Func<string, string>(ExecuteFunction); 6 //開始異步,並指定異步的回調函數 7 func.BeginInvoke("實現了異步", new AsyncCallback(CallBackFunction), "my"); 8 } 9 10 private string ExecuteFunction(string str) //執行函數 11 { 12 Thread.Sleep(2000); 13 // To Do 14 return str; 15 } 16 17 private void CallBackFunction(IAsyncResult ar) //回調函數 18 { 19 //轉化變量類型 20 //因為委托異步編程的類型為AsyncResult類,而這個類又是實現了IAsyncResult接口的,可以說是它的基類! 21 AsyncResult async = ar as AsyncResult; 22 Func<string, string> func = async.AsyncDelegate as Func<string, string>; 23 //獲取異步執行函數的返回值 24 string str = func.EndInvoke(ar); MessageBox.Show(str); 25 } 26 //在最后進行類型轉化的時候,盡量使用“as”進行轉化!
10.2 事件驅動模型實現異步
這個模式的異步編程是所有異步方式中最為復雜的一個,我對它的理解也是很有限的,只限於使用它,不會自己構建它!
基於事件模型的異步不是一個通用型的,只有當需求要進行事件模型異步編程的時候才要進行事件模型異步的創建,主要有兩種方法:
①通過獲取事件中的委托列表,然后通過委托實現異步
首先這個異步思想是基於事件模型的,所以它會對你的事件定義有很大的要求,主要就是那兩個參數的定義,可能會重新定義存儲信息類!
實現思路:
a) 通過事件注冊執行方法
b) 獲取事件依附的委托
c) 最后就是通過委托調用BeginInvoke實現異步
代碼如下:

②通過自定義事件模型實現異步(暫時不會,我會給出幾篇文章參考)
10.3 使用IAsyncResult接口實現異步
使用IAsyncResult接口編程和使用委托異步編程的最大區別:
① 委托異步調用阻塞發生在線程池的工作線程
② IAsyncResult異步調用阻塞方式在線程池的I/O完成線程
Note:其實使用IAsyncRsult接口重要的是對於一些流操作或者一些文件操作都是固定的Begin和End,所以只有當使用那些異步操作的時候才使用它
使用方法跟委托異步很相似,具體的實現代碼就不貼了!
10.4基於BackgroundWorker組建實現異步
Note:其實這個組件最終也是基於事件異步模式進行創建的,所以說它就是封裝了事件異步編程模型,從而使開發者使用更方便!
這個模型使用率比較高,因為微軟都幫我們封裝好了,只要學習一下就會使用它了!
1 private void button1_Click(object sender, EventArgs e) 2 { 3 BackgroundWorker worker = new BackgroundWorker(); 4 //注冊執行函數 5 worker.DoWork += ExecuteFunction; 6 //注冊回調函數 7 worker.RunWorkerCompleted += CallBackFunction; 8 //執行事件,啟動異步操作 9 worker.RunWorkerAsync("通過e.Argument來讀取的"); 10 } 11 12 //執行函數 13 public void ExecuteFunction(object sender, DoWorkEventArgs e) 14 { 15 Thread.Sleep(2000); 16 //因為result的類型為Object 17 //所以可以存入各種對象各種控件對象都可以存入 18 e.Result = "實現異步,這是我存入的值"; 19 string str = e.Argument.ToString(); //這邊的值是跟你啟動異步方法中存入的參數 20 } 21 22 //回調函數 23 public void CallBackFunction(object sender, RunWorkerCompletedEventArgs e) 24 { 25 //讀取存入的值,而這個類似於委托異步編程中獲取執行函數的返回值 26 //相當於委托異步編程中 → 返回值的應用 27 MessageBox.Show(e.Result.ToString()); //讀取 28 }
10.5創建后台線程實現異步
實現思路:
① 創建新的線程,並指定在線程運行的程序
② 設置線程為后台運行,推薦一篇文章:
③ 啟動線程
代碼實現:
1 private void button3_Click(object sender, EventArgs e) 2 { 3 //創建新的線程,並指定它的執行方法 4 Thread thread = new Thread(new ParameterizedThreadStart(MyThreadMethod)); 5 thread.IsBackground = true; 6 thread.Start("aa"); 7 } 8 public void MyThreadMethod(object obj) 9 { 10 //不可以把這個暫停的時間方法創建線程之前,因為你在線程之前暫停那個時候還沒有創建好,不能實現異步,所以這是不可以的 11 Thread.Sleep(2000); 12 MessageBox.Show("這是線程實現異步的"); 13 }
10.6使用線程池實現異步
實現比較簡單,代碼如下:
1 //實現代碼: 2 private void button4_Click(object sender, EventArgs e) 3 { 4 //這句代碼很重要 5 ThreadPool.QueueUserWorkItem(MyThreadMethod, "aaa"); 6 } 7 8 public void MyThreadMethod(object obj) 9 { 10 Thread.Sleep(2000); 11 MessageBox.Show("這是線程實現異步的"); 12 }
10.7六種異步模式的總結
其實這個總結是根據Fish文章進行總結的,o(∩_∩)o 哈哈...
1.異步委托調用:它的實現是將原來需要阻塞的操作交給線程池的工作線程來處理了,此時線程池的工作線程被阻塞了!但是此方法對於依賴【線程池的工作線程】來處理任務的編程模型來說是沒有意義的,比如Asp.Net ,Windows Services以及Web Services這些服務類的編程模型!所以它比較適應一些單線程編程模型,比如Winform這種的單線程!
2.使用IAsyncResult接口:它的實現是將原來需要阻塞的操作交給線程池的I/O完成線程來處理了,所以它適合任何類型。但是有限制,因為不是所有的API都支持此類接口,不過許多的I/O操作是支持此接口的,還有實現起來會比較復雜!
3.基於事件的異步:這種方式可以認為是對其它異步方式的封裝,其主要目的是簡化異步調用模型,使用者可以直接調用事件就能實現異步,如果此模式是對第二種異步方式進行異步封裝,那么它將具體第二種的所有優點!
4.創建新線程的異步:主要特點是在后台創建一個新的線程來執行異步方法,如果有很多用戶同時執行異步操作,那么后台就會創建無數個線程,損害性能,尤其是對服務類的編程模型來說使用起來沒有任何意義,所以在沒有上訴情況下可以考慮使用這種異步方法!
5.使用線程的異步:基本上跟創建新線程類似,僅僅適用於一些桌面程序!
6.使用BackgroundWorker的方式:它的底層也是在使用線程池的工作線程,也是采用的基於事件的異步模式,只不過它使用起來真的很方便,它不是使用的IAsyncResult接口進行異步操作的,只是模式上類似於事件異步模式!
最后:在.NET中標准的異步模式都是使用的IAsyncResult接口,所以后三種並不算真正的異步,但它們卻在某些場合有着很大的作用!
強烈推薦文章:1. C#客戶端的異步操作
2. 我所知道的.NET異步
3. 詳解.NET異步
上面的學不會也找我,:-)
11.LINQ
11.1 LINQ原理剖析
這里主要是關於LINQ家族中:Linq To Object,對它的研究還是比較多的!
LINQ是基於委托的一種高級語法,如果不能正確的理解委托的定義和使用,你就不會真正的理解LINQ和委托天衣無縫的結合,還有一點就是LINQ和迭代器的結合,所以理解委托和迭代器,LINQ你也就會用了!
11.2 Lambda表達式,匿名方法
LINQ也就是一些語法糖,會語法了,LINQ自然而然就會用了,下面就把常用的語法匯總一下,再復習一次:
1 Func<string, string> myFunc = delegate(string str) //匿名方法語法,括號內為參數列表 2 { 3 return "sss"; 4 }; 5 myFunc += strOne => //這邊的strOne是個方法,我沒寫,只要注意參數和返回值一致就可以了 6 { 7 return strOne; 8 }; 9 myFunc += (string strTwo) => //Lambda表達式,指定了參數類型 10 { 11 return strTwo; 12 }; 13 myFunc += (strThree) => //Lambda表達式,也可以不指定類型,系統會自動檢測 14 { 15 return strThree; 16 }; 17 myFunc += strFour => "ssssss"; //不需要加“return”,因為編譯器會幫你自動加上去的! 這樣寫法就搞急了,放眼一看說真的一開始還真看不懂! 18 Action myAction = () => Console.WriteLine("ssssssssss"); 19 myAction += () => Console.WriteLine("sssssss"); //如果沒有參數,直接使用括號就可以了 20 myAction += delegate() 21 { 22 Console.WriteLine("ssssssssssssss"); 23 };
注意事項:①如果有參數的話,必須寫明參數變量!
②可以不寫參數類型,編譯器會自動判斷!
③如果沒有參數必須要寫一個空的括號,這樣才說明沒有參數!
④如果方法中只有一行代碼,可以不要花括號,反之則要!
11.3 LINQ擴展方法匯總
Family |
Query Operations |
Filtering |
OfType, Where |
Projection |
Select,SelectMany |
Partitioning |
Skip, SkipWhile, Take, TakeWhile |
Join |
GroupJoin, Join |
Concatenation |
Concat |
Ordering |
OrderBy, OrderByDescending, Reverse, ThenBy, ThenByDescending |
Grouping |
GroupBy, ToLookup |
Set |
Distinct, Except, Intersect,Union |
Conversion |
AsEnumerable, AsQueryable, Cast, ToArray, ToDictionary, ToList |
Equality |
SequenceEqual |
Element |
ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault |
Generation |
DefaultIfEmpty, Empty, Range, Repeat |
Quantifiers |
All, Any, Contains |
Aggregation |
Aggregate, Average, Count, LongCount, Max, Min, Sum |
11.4 LINQ詳解方法使用細節
①寫個我覺得比較難的“Group By”方法
分組語法最難理解的就是對“key”的理解,以及“into”關鍵字后面包含的是什么東西(就是我們分組后要使用的數據源),以及理解“by”關鍵后后面就是存放“key”的地方,代碼示例:
1 var queryTwo = from book in SampleData.Books 2 group new { book.Title, book.PageCount } //分組的關鍵字 3 by new { Name = book.Publisher.Name, Subject = book.Subject.Name } //給分組關鍵字設置別名 4 into newBook //分組之后的數據源 5 select new { Books = newBook }; 6 7 //使用嵌套“foreach”來循環遍歷 8 foreach (var item in queryTwo) 9 { 10 Console.WriteLine(item.Books.Key); 11 foreach (var book in item.Books) 12 { 13 Console.WriteLine(book.Title + " " + book.PageCount); 14 } 15 }
總結:理解 → group A by B into C (A:數據源中的關鍵字 B:分組之后給關鍵字去的別名 C:分組之后的數據源)!
因為最后的newBook是我的最終數據源,上面也提過最終數據源是包括所有分組的key,所以在調用key的時候應該拿最終的數據源集合調用,還有當大於1個分組關鍵字時進行讀取的時候會以這樣的方式出現{ Name=Book,Subject=aaa},還有大部分情況遍歷分組LINQ查詢都會使用嵌套的Froeach語句來檢索數據!
推薦一篇文章: 你能指出這個 ForEach 擴展方法中的錯誤嗎? ,其實可以做的更好,我需要不斷的學習啊...
11.5 自定義LINQ擴展方法
如果想寫自定義LINQ方法,無非就是繼承IEnumerable接口的擴展方法,寫一個我們常用的foreach,這樣可以節省我們好多的foreach代碼,如下代碼:
1 public static class Program 2 { 3 static void Main(string[] args) 4 { 5 string[] strings = { "a", "b", "c" }; 6 strings.MyForeach(Console.WriteLine); //括號里面是個方法 7 } 8 9 public static void MyForeach<T>(this IEnumerable<T> source, Action<T> func) 10 { 11 foreach (T item in source) //先循環遍歷我的數據源,然后把每個數據方法我的匿名方法中,從而輸出值! 12 { 13 func(item); //item是參數 14 } 15 } 16 }
總結:寫代碼無時無刻,但是我們最關注是對代碼的思考,對代碼的感悟,思想的提升,不管怎么樣,寫完之后停下來想想,想什么全由個人來定,有人想性能,有人想重構,想優化,想簡潔,只有努力過后思想強大了才能在編程道路上得心應手!
本人也研究了一些關於Linq To SQL的知識,可惜學藝不精啊!
強烈推薦文章:1. LINQ體驗系列文章導航
3. LINQ之路系列博客導航
看完上面三個系列,學不會找我,:-)
12.IO
這個系列我沒有系統的學習過,不過園子里面有個人寫的很好,也正在學習中,推薦文章:C# 溫故而知新:Stream篇(—)
下面是我整理的流操作系統類圖:
13.File
熟悉文件系統的操作是學習.NET Frameword必不可少的一部分,我沒有系統的學習過這些知識,但能運用一些常見方法進行項目開發,下面是文件系統主要的類圖架構,理解它們之間的關系,相信學習起來也很方便啦!
推薦文章:細細品味C#——文件操作
14.代碼整潔之道
最近看完了經典之作:Code Clean,收獲很多,分享一下我的感悟,主要是代碼規范,設計方面的知識!
整潔代碼只做一件事,糟糕的代碼邏輯混亂,想做很多事,導致了閱讀,修改困難!
14.1 整潔代碼的基本規則:
①通過所有的單元測試
②沒有重復代碼,如果同一段代碼出現了兩次以上,那就是提醒你該提取相同代碼進行重構了,時刻提醒自己不要重復
③體現系統設計的理念
④有意義的命名
14.2 注重代碼的讀與寫,它們的比例是“10:1”
14.3 抽離try/catch代碼快,這個非常贊同這個原則, 上圖:
14.4 關於注釋(書中講到的注釋規則讓我很有同感,因為現在項目中就有這樣的現象)
請注意:注釋也許真的不需要,學會使用代碼就能完整表達設計和邏輯意圖!
常見現象:代碼在變動,在演化,彼此分離和重合,可是注釋並不總是隨着變動,上圖:
所以只有代碼才能真正的告訴你它在干什么,它有什么作用!
Note:與其花時間編寫解釋你那糟糕代碼的注釋,還不如花時間清潔那糟糕的代碼!
壞注釋和多余注釋的幾點原則:
①有時候一段壞的注釋不緊會影響代碼的整潔,而且還會占用一定的時間,最終讀注釋的時間比閱讀代碼的時間還長,所以這種注釋要刪除它,影響我們閱讀代碼的時間
②日志式注釋:這種注釋最有感觸,在class開頭寫上每次修改的記錄,這種方式也有好處,但是這種情況應該在沒有源代碼控制的情況下進行記錄(其實我聽贊同在class頭上寫上每次修改的版本的)
③關於廢話性和誤導性的注釋堅決不能存在
④能用函數和變量是就別用注釋,所以變量和函數的命名真的很重要,可以讓人一眼就能看出它的作用
請明白非常重要的一點:注釋的作用就是解釋未能自行解釋的代碼,如果注釋本身還需要解釋,就太遺憾了!
14.5 單元測試的重要性
一直覺得單元測試可有可無,那是因為我只是學習過從來沒有真正的在項目中運用過,可是最近我下了狠心要在項目中構建一個單元測試框架,終於被我搞定了,我也感悟到單元測試對一個開發人員的重要性,想學習的話可以看看這篇文章:走進單元測試五:單元測試文章系列目錄
建議大家看看<<代碼整潔之道>>和<<.NET設計規范>>以及<<程序猿修煉之道之單元測試>>
15.其它知識點
15.1 const和readonly本質區別
理解兩者是在“編譯時”還是“運行時”常量,以及兩者的作用域,那么它們將不會這么神秘!
編譯時OR運行時:
const:編譯時
readonly:運行時
作用域:
const:①本身就是靜態變量
②只能定義基本類型,如int,string等等
③局部變量和全局變量都可以定義
④一旦定義就不能修改
readonly:①不是靜態變量,如果需要需加上“static”關鍵字
②可以定義一切類型,可以是自己自定義的對象
③只能定義全局變量
④一旦定義可以在構造函數里面進行初始化變量
總結:園子里面還有很多對於它們性能方面的文章,有興趣的可以搜搜看,推薦使用“readonly”吧!
15.2 is和as操作符
16.2.1 As和強制轉化最本質的區別
As:進行轉換的時候永遠不是出現異常,當轉換失敗時會返回一個“null”值,所以你只需要進行一個null值的判斷就知道轉換失敗還是成功了!
強制轉化:會出現轉換失敗並拋出異常,所以我們都需要使用“try/catch”來捕獲轉換出錯的異常,也可以使用“is”來判斷是否是你要轉換的類型!
16.2.2 一些常見注意點
① as不能用於值類型的轉化
如:object number=100;int numberOne = number as int;
這是因為如果轉換失敗,那么就會返回一個“null”值,但是值類型是不允許為“null”的,所以在語法上是行不通的,就是你寫成了“int?”也是不行的!
② 使用Is配合強制轉換來進行類型轉換
首先使用“Is”來判斷是否是我需要轉換的類型,然后在進行強制轉換

③ 在沒有泛型的foreach中,也是把“object”進行強制轉化成所需要的類型,代碼如下:

Note:或者使用GetType()方法來精確檢測是否是你想要的轉換類型!
15.3 運算符操作以及類型轉化操作重載
這兩個知識點還是比較容易學習的,一個是操作符的重載,一個是用於自定義強制轉換的方法(你也可以使用“as”進行強制轉換),只要稍加注意一些語法就好了!
1 public class MyPerson 2 { 3 public string Name { get; set; } 4 5 /* 6 * ①必須為靜態 7 * ②關鍵字operator 8 * ③需要定義重載的操作符 9 * ④定義返回值類型 10 */ 11 public static MyPerson operator +(MyPerson personFather, MyPerson personMother) 12 { 13 return new MyPerson() { Name = personFather.Name + personMother.Name }; 14 } 15 16 /* 17 ①必須為靜態 18 * ②關鍵字“explict”和operator” 19 * ③需要轉化的類型:MyPerson 20 */ 21 public static explicit operator MyPerson(MyPeople myPeople) 22 { 23 return new MyPerson(){ Name="YCG" }; 24 } 25 } 26 public class MyPeople 27 { }
具體的用法如下:
1 MyPerson personOne = new MyPerson() { Name = "AAAAAA" }; 2 MyPerson personTwo = new MyPerson() { Name = "BBBBB" }; 3 MyPerson personThree = personOne + personTwo; //操作符重載 4 Console.WriteLine(personThree.Name); 5 6 MyPeople people = new MyPeople() { Name = "wang wei" }; 7 MyPerson personFour = (MyPerson)people; //類型強制轉換 8 Console.WriteLine(personFour.Name);
15.4 ToString方法
入口: c# 擴展方法奇思妙用高級篇五:ToString(string format) 擴展
15.5 數據實體模型(Tuple)以及匿名類型
16.5.1 Tuple實際上就是一個匿名的實體的模型,它的用處在於不要自己定義一個實實在在的Entity,使用它就能達到效果!

Note:在查看源代碼的時候注意它的第八個參數:
16.5.2 匿名類型
1 var data = new { number = 11111, str = "ssssss" }; //不需要定義變量的類型,如果想知道匿名類型底層源碼怎么寫的,可以使用反編譯查看源碼,一目了然了! 2 Console.WriteLine(data.number + data.str);
15.6 String和StringBuilder詳解
這個技術大妞們已經討論的很多,我也沒這個能力說的一清二楚,推薦幾篇文章吧:1. 字符串的駐留(String Interning) 2. 深入理解string和如何高效地使用string 等等,實在很多啦...