1. 程式人生 > >【Unity】Effective C#觀後感之提高Unity中C#程式碼質量的21條準則

【Unity】Effective C#觀後感之提高Unity中C#程式碼質量的21條準則

我們知道,在C++領域,作為進階閱讀材料,必看的書是《Effective C++》。 而《Effective C#》之於C# ,是類似《Effective C++》之於C++一樣的存在。

這篇文章,將《Effective C# Second Edition》一書中適用於Unity遊戲引擎裡使用C#的經驗之談進行了提煉,總結成為21條(一開始總結的是22條,後來發現第22條也是.NET的特性,Unity版本的mono並沒有實現,所以嚴格意義上來說是21條)準則,供各位快速地掌握這本書的知識梗概,在Unity中寫出更高質量的C#程式碼。

《Effective C# Second Edition》一書原本有50條原則,但這50條原則是針對C#語言本身以及.NET來寫的,我在閱讀過程中,發現是有些原則並不適用於Unity中mono版本的C#的使用。於是,在進行讀書筆記總結的時候,將不適用的原則略去,同時將適用的原則進行提煉,總結出21條,構成本文的內容。

需要注意,因為是挑出了書中適用的準則,導致準則序號有些跳躍,為了閱讀方便,本文對這些序號進行了重新排列。重排後,標題中與書中序號不一樣的準則,都在該原則總結的末尾註明瞭對應的原書序號。

同樣地,作為總結式文章,每一條的內容都高度概括,也許理解坡度比較陡,若有讀到不太理解的地方,建議大家去閱讀原書,英文版和中文版均可,看看原書中提供的各種程式碼與示例,這樣掌握起來就會事半功倍。

本文內容思維導圖式總結

以下是本文內容,提高Unity中C#程式碼質量的22條準則的總結式思維導圖

原則1

儘可能地使用屬性而不是可直接訪問的資料成員

● 屬性(property)一直是C#語言中比較有特點的存在。屬性允許將資料成員作為共有介面的一部分暴露出去,同時仍舊提供面向物件環境下所需的封裝。屬性這個語言元素可以讓你像訪問資料成員一樣使用,但其底層依舊是使用方法實現的。

● 使用屬性,可以非常輕鬆的在get和set程式碼段中加入檢查機制。

需要注意,正因為屬性是用方法實現的,所以它擁有方法所擁有的一切語言特性:

1)屬性增加多執行緒的支援是非常方便的。你可以加強 get 和 set 訪問器(accessors)的實現來提供資料訪問的同步。

2)屬性可以被定義為virtual。

3)可以把屬性擴充套件為abstract。

4)可以使用泛型版本的屬性型別。

5)屬性也可以定義為介面。

6)因為實現實現訪問的方法get與set是獨立的兩個方法,在C# 2.0之後,你可以給它們定義不同的訪問許可權,來更好的控制類成員的可見性。

7)而為了和多維陣列保持一致,我們可以建立多維索引器,在不同的維度上使用相同或不同型別。

無論何時,需要在型別的公有或保護介面中暴露資料,都應該使用屬性。如果可以也應該使用索引器來暴露序列或字典。現在多投入一點時間使用屬性,換來的是今後維護時的更加遊刃有餘。

原則2

偏向於使用執行時常量而不是編譯時常量

對於常量,C#裡有兩個不同的版本:執行時常量(readonly)和編譯時常量(const)。

應該儘量使用執行時常量,而不是編譯器常量。雖然編譯器常量略快,但並沒有執行時常量那麼靈活。應僅僅在那些效能異常敏感,且常量的值在各個版本之間絕對不會變化時,再使用編譯時常量。

編譯時常量與執行時常量不同之處表現在於他們的訪問方式不同,因為Readonly值是執行時解析的:

● 編譯時常量(const)的值會被目的碼中的值直接取代。

● 執行時常量(readonly)的值是在執行時進行求值。● 引用執行時生成的IL將引用到readonly變數,而不是變數的值。

這個差別就帶來了如下規則:

● 編譯時常量(const)僅能用於數值和字串。

● 執行時常量(readonly)可以為任意型別。執行時常量必須在建構函式或初始化器中初始化,因為在建構函式執行後不能再被修改。你可以讓某個readonly值為一個DataTime結構,而不能指定某個const為DataTIme。

● 可以用readonly值儲存例項常量,為類的每個例項存放不同的值。而編譯時常量就是靜態的常量。

● 有時候你需要讓某個值在編譯時才確定,就最好是使用執行時常量(readonly)。

● 標記版本號的值就應該使用執行時常量,因為它的值會隨著每個不同版本的釋出而改變。

● const優於readonly的地方僅僅是效能,使用已知的常量值要比訪問readonly值略高一點,不過這其中的效率提升,可以說是微乎其微的。

綜上,在編譯器必須得到確定數值時,一定要使用const。例如特性(attribute)的引數和列舉的定義,還有那些在各個版本釋出之間不會變化的值。除此之外的所有情況,都應儘量選擇更加靈活的readonly常量。

原則3

推薦使用is 或as操作符而不是強制型別轉換

● C#中,is和as操作符的用法概括如下:

is : 檢查一個物件是否兼容於其他指定的型別,並返回一個Bool值,永遠不會丟擲異常。

as:作用與強制型別轉換是一樣,但是永遠不會丟擲異常,即如果轉換不成功,會返回null。

● 儘可能的使用as操作符,因為相對於強制型別轉換來說,as更加安全,也更加高效。

● as在轉換失敗時會返回null,在轉換物件是null時也會返回null,所以使用as進行轉換時,只需檢查返回的引用是否為null即可。

● as和is操作符都不會執行任何使用者自定義的轉換,它們僅當執行時型別符合目標型別時才能轉換成功,也不會在轉換時建立新的物件。

● as運算子對值型別是無效,此時可以使用is,配合強制型別轉換進行轉換。

● 僅當不能使用as進行轉換時,才應該使用is操作符。否則is就是多餘的。

原則4

 推薦使用條件屬性而不是#if條件編譯

● 由於#if/#endif很容易被濫用,使得編寫的程式碼難於理解且更難於除錯。C#為此提供了一條件特性(Conditional attribute)。使用條件特性可以將函式拆分出來,讓其只有在定義了某些環境變數或設定了某個值之後才能編譯併成為類的一部分。Conditional特性最常用的地方就是將一段程式碼變成除錯語句。

● Conditional特性只可應用在整個方法上,另外,任何一個使用Conditional特性的方法都只能返回void型別。不能再方法內的程式碼塊上應用Conditional特性。也不可以在有返回值的方法上應用Conditional特性。但應用了Conditional特性的方法可以接受任意數目的引用型別引數。

● 使用Conditional特性生成的IL要比使用#if/#Eendif時更有效率。同時,將其限制在函式層面上可以更加清晰地將條件性的程式碼分離出來,以便進一步保證程式碼的良好結構。

原則5

理解幾個等同性判斷之間的關係

● C#中可以建立兩種型別:值型別和引用型別。如果兩個引用型別的變數指向的是同一個物件,它們將被認為是“引用相等”。如果兩個值型別的變數型別相同,而且包含同樣的內容,它們被認為是“值相等”。這也是等同性判斷需要如此多方法的原因。

● 當我們建立自己的型別時(無論是類還是struct),應為型別定義“等同性”的含義。C#提供了4種不同的函式來判斷兩個物件是否“相等”。

1)public static bool ReferenceEquals (object left, object right);判斷兩個不同變數的物件標識(object identity)是否相等。無論比較的是引用型別還是值型別,該方法判斷的依據都是物件標識,而不是物件內容。

2)public static bool Equals (object left, object right); 用於判斷兩個變數的執行時型別是否相等。

3)public virtual bool Equals(object right); 用於過載

4)public static bool operator ==(MyClass left, MyClass right); 用於過載

● 不應該覆寫Object.referenceEquals()靜態方法和Object.Equals()靜態方法,因為它們已經完美的完成了所需要完成的工作,提供了正確的判斷,並且該判斷與執行時的具體型別無關。對於值型別,我們應該總是覆寫Object.Equals()例項方法和operatior==( ),以便為其提供效率更高的等同性判斷。對於引用型別,僅當你認為相等的含義並非是物件標識相等時,才需要覆寫Object.Equals( )例項方法。在覆寫Equals( )時也要實現IEquatable<T>。

PS: 此原則對應於《EffectiveC# Second Edition》中原則6。

原則6

瞭解GetHashCode( )的一些坑

● GetHashCode( )方法在使用時會有不少坑,要謹慎使用。GetHashCode()函式僅會在一個地方用到,即為基於雜湊(hash)的集合定義鍵的雜湊值時,此類集合包括HashSet<T>和Dictionary<K,V>容器等。對引用型別來講,索然可以正常工作,但是效率很低。對值型別來講,基類中的實現有時甚至不正確。而且,編寫的自己GetHashCode( )也不可能既有效率又正確。

● 在.NET中,每個物件都有一個雜湊碼,其值由System.Object.GetHashCode()決定。

● 實現自己的GetHashCode( )時,要遵循上述三條原則:

1)如果兩個物件相等(由operation==定義),那麼他們必須生成相同的雜湊碼。否則,這樣的雜湊碼將無法用來查詢容器中的物件。

2)對於任何一個物件A,A.GetHashCode()必須保持不變。

3)對於所有的輸入,雜湊函式應該在所有整數中按隨機分別生成雜湊碼。這樣雜湊容器才能得到足夠的效率提升。

PS: 此原則對應於《EffectiveC# Second Edition》中原則7。

原則7

理解短小方法的優勢

將C#程式碼翻譯成可執行的機器碼需要兩個步驟。

C#編譯器將生成IL,並放在程式集中。隨後,JIT將根據需要逐一為方法(或是一組方法,如果涉及內聯)生成機器碼。短小的方法讓JIT編譯器能夠更好地平攤編譯的代價。短小的方法也更適合內聯。

除了短小之外,簡化控制流程也很重要。控制分支越少,JIT編譯器也會越容易地找到最適合放在暫存器中的變數。

所以,短小方法的優勢,並不僅體現在程式碼的可讀性上,還關係到程式執行時的效率。

PS:此原則對應於《EffectiveC# Second Edition》中原則11。

原則8

選擇變數初始化而不是賦值語句

成員初始化器是保證型別中成員均被初始化的最簡單的方法——無論呼叫的是哪一個建構函式。初始化器將在所有建構函式執行之前執行。使用這種語法也就保證了你不會再新增的新的建構函式時遺漏掉重要的初始化程式碼。

綜上,若是所有的建構函式都要將某個成員變數初始化成同一個值,那麼應該使用初始化器。

PS: 此原則對應於《Effective C# Second Edition》中原則12。

相關推薦

Effective C#觀後感提高UnityC#代碼質量21準則

們的 嚴格 知識 將不 實現接口 控制流程 effect 序列 狀態 轉自:http://blog.csdn.net/swj524152416/article/details/75418162 我們知道,在C++領域,作為進階閱讀材料,必看的書是《Effective C++

UnityEffective C#觀後感提高UnityC#程式碼質量21準則

我們知道,在C++領域,作為進階閱讀材料,必看的書是《Effective C++》。 而《Effective C#》之於C# ,是類似《Effective C++》之於C++一樣的存在。這篇文章,將《E

資料視覺化Matplotlib : pandas的繪圖函式

Pandas有許多能夠利用DataFrame物件資料組織特點來建立標準圖表的高階繪圖方法,本文主要介紹的是pandas中的繪圖函式。 #coding:utf-8 import matplotlib.pyplot as plt import pandas as pd import numpy as np fr

使用四大外掛提高Android程式碼質量

由於之後開發將會使用Android studio進而替代Eclipse作為高效的Android開發工具,所以今天分享一篇關於利用四種外掛 lint、findBugs、PMD、 CheckStyles 的功能和使用方式,以及如何將多個外掛整合在一起的文章”教

專欄 - Effective C++第三版快速閱讀

Effective C++第三版快速閱讀 內容簡介:有人說C++程式設計師可以分為兩類,讀過Effective C++的和沒讀過的。當您讀過《Effective C++:改善程式與設計的55個具體做法(第3版)(中文版)(雙色)》

專欄 - Effective C++程式設計

Effective C++程式設計 記錄《Effective C++ 中文版 第二版》、《Effective C++中文版 第三版》、《More Effective C++中文版》。以及自己經常看到的一些使用技巧。

UnityVisual Studio Code常備快捷鍵

Visual Studio Code是個牛逼的編輯器,啟動非常快,完全可以用來代替其他文字檔案編輯工具。又可以用來做開發,支援各種語言,相比其他IDE,輕量級完全可配置還整合Git感覺非常的適合前端開發。 所以我仔細研究了一下文件未來可能會作為主力工具使用。 主命令框

ShiroApache Shiro架構身份認證(Authentication)

trac pretty asm 安全保障 軟件測試 釋放 model tac 讀取配置文件 Shiro系列文章: 【Shiro】Apache Shiro架構之權限認證(Authorization) 【Shiro】Apache Shiro架構之集成web

Mysql經常使用指令——忘記password

my.cnf 執行cmd mysql blank lan service 重裝 授權 環境變量 上一篇文章基本總結了下myql下通過指令怎麽創建用戶。詳見:【Mysql】經常使用指令之——用戶操作(創建,授權,改動。刪除) 今天說下特殊情況,忘記passw

JavaSE--網絡安全證書、密鑰、密鑰庫等名詞解釋

detail 發的 都是 base64 request 服務器 win art ive 轉載:http://www.cnblogs.com/alanfang/p/5600449.html 那些證書相關的名詞解釋(SSL,X.509,PEM,DER,CRT,CER,KEY,

中文分詞HMM模型詳解

實現 含義 jieba 順序 清晰 bsp 中國 matrix 統計 關於HMM模型的介紹,網上的資料已經爛大街,但是大部分都是在背書背公式,本文在此針對HMM模型在中文分詞中的應用,講講實現原理。 盡可能的撇開公式,撇開推導。結合實際開源代碼作為例子,爭取做到雅俗共賞,

AIX系統錯誤--磁盤錯誤

scsi 系統 article root into flag wsh digg kaa AIX系統錯誤之--磁盤錯誤 來源:http://blog.csdn.net/yujin2010good/article/details/40075485 系統環境: 操作系統

轉載python3安裝scrapywindows32位爬坑

命令 cep 3.2 wheel 跳過 。。 學習 自己的 打開 python3安裝scrapy之windows32位爬坑 原創 2016年11月06日 01:38:08 標簽: scrapy / windows / python / 開源框架 / 網絡爬蟲

文件下載斷點續傳(客戶端與服務端的實現)

http協議 當前時間 end box [] ada demo 服務端 sem 【轉】文件下載之斷點續傳(客戶端與服務端的實現) 【轉】文件下載之斷點續傳(客戶端與服務端的實現) 前面講了文件的上傳,今天來聊聊文件的下載。 老規矩,還是從最簡單粗暴的開始。那麽多簡單算簡單

javascriptjavascript設計模式單例模式

函數 設置 tin 私有 資源 公有 key fun 初始化 單例模式: 定義:單例模式之所以這麽叫,是因為它限制一個類只能有一個實例化對象。 實現方法:判斷實例是否存在,如果存在則直接返回,如果不存在就創建了再返回。(確保一個類只有一個實例對象) 特點: 命名空間提供者,

javascriptjavasrcipt設計模式策略模式

影響 配置 可能 pan style 類庫 ava 自由 AC 策略模式支持在運行時由使用者選擇合適的算法,對於使用者而言不用關心背後的具體事項,而使用者自動根據當前程序執行的上下文和配置,從已有的算法列表中選擇出合適的算法來處理當前任務。 1.要解決的問題 2.如何實現

20180417ELK日誌管理filebeat收集分析mysql慢日誌

filebeat slow log pipeline slowlog 環境版本 filebeat: 6.2.3mysql: 5.6.38 錯誤信息 { "_index": "mysql-slow-2018.04.17", "_type": "doc", "_id": "AWLRiD

ASP.NET CORE系列Entity Framework Core 數據庫遷移

snapshot 文章 src AD data 上下 ont pre 發生 前言 最近打算用.NET Core寫一份簡單的後臺系統,來練練手 然後又用到了Entity Framework Core 發現園子裏有些文章講得不是那麽細節,對於新手小白來說,可能會有點懵。 特意

Linux定時任務crontab

數據備份 res 整數 用戶數 mailto 加載 -c 維護 mini linux 系統則是由 cron (crond) 這個系統服務來控制的。Linux 系統上面原本就有非常多的計劃性工作,因此這個系統服務是默認啟動的。另 外, 由於使用者自己也可以設置計劃任務,所以,

原創分布式redis復習精講

復雜 雙緩存 緩存 方便 參數 過期策略 內心 線程 註意 引言 為什麽寫這篇文章? 博主的《分布式之消息隊列復習精講》得到了大家的好評,內心誠惶誠恐,想著再出一篇關於復習精講的文章。但是還是要說明一下,復習精講的文章偏面試準備,真正在開發過程中,還是腳踏實地,一步一個腳印