---
# System prepended metadata

title: Coding Style
tags: [coding style]

---

Coding Style
===
### 目的
1. 好維護
2. 增加易讀性
3. 安全

### C# 強型別語言


### C# & .Net 內置型別
short 與右邊一樣意思，但不要使用右邊 not System.Int16
...
 
### 類別
類別成員：欄位(Field)、建構子、屬性(Property)、方法、事件、解構子(查查)

### 存取修飾詞
C#不寫存取修飾詞的話:
class defalut == internal
class成員 default == private
都要使用到修飾詞
internal : 包含在同一個"組件"
* 查查命名空間和組件

### Field & property
#### Field 
比較像類別內部的全域變數，會在物件還未執行建構子時就生成，一般在類別中都是private。
我們在取得或修改Field，都需要藉由Property來對Field做操作，不可直接操作Field。
static readonly field 非常接近於常數(const)，唯一差別在於常數不需要依存在物件身上，類別撰寫編譯成功後即佔有一個記憶體空間。
#### Property
Property 可以下XML註解、和可以看到有多少人參考
:::info
**Field 與 Property 差異**

:::

### 數字抽出來，不要有奇怪的數字
1. 列舉(enum)
2. const
3. 區域變數

### Don't copy
1. 冗贅
2. 維護性低
3. 不知道寫啥，複製錯
4. 懶惰了

### 類別存放規則
1. 一個類別只放一個cs檔
2. POCO(Plain Old C#Object”或“Plain Old CLR Object)
只定義了屬性的資料類型
4. Partial類別 禁用

### Static
程式層級 data(global) code stack heap
static在創建時，即在data中創建
靜態類別無法建立Instance
全域且唯一 => 須考慮Thread safe

### 寫作邏輯
每個邏輯之間用"空行"分隔

### 交付原則
錯誤警告訊息都為０才能交出去

### 命名原則
1. const 一律全大寫，底線分隔
2. 區域變數、方法參數：小寫開頭，駝峰命名
3. 其餘，大寫開頭，駝峰命名

1. 方法：動詞大寫開頭，類別以及類別成員：名詞
2. bool：含有判斷意義 Is Can Has開頭

1. 類別成員屬性，不要再冠上類別名稱
2. 盡量使用有意義的變數名稱
3. linq、lambda中也盡量要有意義
4. 縮寫以大家認同的為主
5. Interface一律以I開頭，範型的型別參數一律使用"<\T>"
6. 命名空間，盡量與專案名稱和組件一致

### 註解
多行註解以
```csharp=
// 多行註解
// 多行註解
...
```

:::warning
[What is self-documenting code and can it replace well documented code?](https://stackoverflow.com/questions/209015/what-is-self-documenting-code-and-can-it-replace-well-documented-code)
* 作業：self-documenting是什麼?
不需要經由註解就能夠讓其他程式設計師理解的程式碼
* why self-documenting?
1. 只需要少量的註解，
* How to self-documenting?
1. 名稱定義清楚，包含變數以及方法名稱
2. 程式撰寫邏輯拉成各個方法
3. 方法要做的事情單一化
:::

### 工作清單
#### TODO
尚待完成的任務
#### UNDONE
還沒寫完的部分，不要有這個!!請寫完
#### HACK
目前還不好的寫法，需要修改

### 型別
* Primitive Type基本型別
* Value Type
    * struct
    * int
    * char
    * double
* Referance Type
    * string

### Nullable Type
* null
不能被比較，若有一堆資料要找出等於10或不等於10的資料，欄位為null的欄位都不會被算進去。
* nullable type
0 與 null 的意義不同時會用到。
```
int? a = 1; // 會轉變成參考型別
```
* 打擊率舉例
有一位打擊者的打擊率是0跟是null，表示不一樣的事情。打擊率0表示是真滴打不到；打擊率null表示還未有此打擊者資料或遺失，不表示這位打擊者真滴打不到。

### 浮點數誤差
10進位轉float怎麼轉，為何有誤差？
為什麼會有浮點數誤差？
:::info
* 作業：浮點數是怎麼呈現？
[關於浮點數誤差與IEEE-754](http://davidhsu666.com/archives/ieee-754/)
根據IEEE754，一個float以4 bytes(32bits)儲存，主要分為三個部分，正負號Sign(1bit)、指數Exponent(8bits)、尾數Mantissa(23bits)
轉換流程：
1. 將10進位數字寫為2進位表示法，再將其轉為以2為底數的科學記號表示法(正規化)
2. 正負號部分(0為正、1為負)、指數部分(需要加上2進位的127，原因在於中間有8個位元來表示指數部分，原本呈現範圍應為-127-128之間，但在此要表示指數部分不需要呈現正負號，即加上127將範圍轉為0-255，用意在於將有正負的數字(-127-128)轉為不需要以正負號(0-255)呈現的方式)、尾數部分則為科學記號後的小數部分
* 誤差來源來自於"尾數"被截斷的部分，
* 浮點數轉10進位
* 一些浮點數的特殊值
    * Infinity
    * NaN
    * ...
:::
### 匿名型別
* 作業(好處和差異並實作)
:::info
* 什麼是匿名型別?
由編譯器將唯獨資料組轉換為物件的方式，型別名稱由編譯器自動賦予，其身上的屬性會由編譯器賦予合適的型別。匿名型別不可宣告於類別成員當中，即不能在物件建構的屬性、欄位中宣告。並且基於同樣原因，匿名型別只能宣告於大括號中，表示其生命週期與區域變數一致。
* 匿名型別是否能在其屬性中宣告某一屬性為null或是其他的參考型別?
無法宣告為null，但能夠宣告某一屬性變數去承接其他的參考型別，原因在於編譯器會自動去賦予合適的型別，而null並非一個型別，而是完全的空(記憶體位置指向0x0000之類的)。
```csharp=
var result = new { ID = "123", Name = "kj" };
```
* var 與 匿名型別
因為沒有一個合適的類別來承接編譯器所自動分配的類別，因此匿名型別與var可以算是互相綁定。
:::
### 隱含型別
:::info
* 什麼是隱含型別?
使用var關鍵字宣告的變數，在編譯的時機點才會被賦予型別，事實上var是有其型別的。由編譯器來推斷最合適的型別。常用於Linq中。
唯一有一個小小小的缺點在於，因為是由編譯器自動推斷其型別，所以可能對於記憶體的控管不夠精準。
若是要給讓編譯器自動將型別推斷為某個型別，則需要將數字後面加上一些特殊字元(只限於一些基本型別)。
```csharp=
var i = 10;
var j = 10l; // long
// 編譯器會推斷 i 為int，但是可能事實上他只需要用short來存即可，此時即造成記憶體浪費
```
:::
* 作業(好處和差異並實作)
### 泛型
* 作業(好處和差異並實作)
:::info
* 什麼是泛型?

```csharp=
class TestList<T>
{
    internal List<T> list = new List<T>(); 

    public void Add(T value)
    {
        list.Add(value);
    }

    public T Get(int index)
    {
        return this.list[index];
    }
    public void print()
    {
        foreach(T value in this.list)
        {
            Console.WriteLine(value.ToString());
        }
    }
}
```
* 泛型的優點?
:::

### Dynamic
編譯時期不檢查型別，脫離強型別的範疇。少用。

### Boxing & Unboxing
value type -> reference type
```csharp=
int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;   // unboxing
```
配置記憶體給reference type的過程耗費效能

### 隱含轉換 & 明確轉換
隱含轉換：不必使用特殊語法，且不會有遺失資料的可能的安全轉換

明確轉換：不安全，可能會造成資料遺失，避免

#### 正確的明確轉換寫法
* as (old，要被淘汰)不要用
* is (good)
```csharp=
if (orange_1 is Fruit){ // 會做判斷傳回 boolean
    // do something
}
// 如果後面是null則為false
```

### Overloading
同樣名稱的方法可以傳入不同參數、不同參數數量

### 選擇性參數
原先有預設的參數，如果沒有傳入，則會採用預設值

### 具名引數
可以在參數給予的時間點，根據特定的傳入名稱來給值，因此可以不需要依照傳入參數的順序來給予。

### Call by value & Call by reference
stack : assign值
heap : allocate記憶體空間
* ref 傳遞自己的記憶體位置進去

#### 字串不變性!!!!
因為heap中有string pool的存在!!!!!
```csharp=
string name = "Jake";
func.SetValue("Tom");
func.SetValue(ref name);
```

### Tuple
[ValueTuple的東東](https://www.tutorialsteacher.com/csharp/valuetuple)
```csharp= 
// old
Tuple<int,string, string> tuple = 
    new Tuple<int, string, string>(1, "QQ", "SSS");
Console.WriteLine(tuple.Item1); // 1
// new
(int, string, string) tuple = Tuple.Create(1, "QQ", "SSS");
```
效能以及可讀性較差。

### params
可以傳入多個不特定長度的參數
少用，會喪失多載的擴充性
```csharp=

```
### 空字串
採用 string.Empty
不要用 ""

### @
處理跳脫字元，有點像python中的 "r"

### 字串串接!!!
string的本質是char array
直接串接會一直去尋找空的連續記憶體來存放新的字串(每串接一個新字元就成為一個新字串)，如果要串接起來的字串超級長，會持續將記憶體空間佔用，限縮後來的新字串空間，因此空間會越找越久，也有可能會有記憶體不足的問題。

採用StringBuilder會更快，比較像linkedList直接指向每個字串片段的記憶體位置。

### string format
採用 $ sign做變數與字串串接
```csharp=
string stockId = "1702";
string stockName = "南僑";
string comboBoxHeader = $"{stockId} - {stockName}";
```

### 字串比較
可以查找
* String.Compare()
有許多已經寫好的方法
* try一下

### 字串尋找演算法
* 科普一下

### 擴充方法
加上this可以擴充後面型別身上的方法
* 練習一下

### 記憶體配置
code、data、stack、heap
32 bit & 64 bit 會影響每一個word(4 byte or 8 byte)的大小

### 釋放資源
stack會在沒有參照後就把變數刪除
heap則不會，會持續存在於heap中，等待gc機制回收
但是gc的成本很高，若是採用dispose()的方式來回收物件，會造成效能大幅降低。
盡量避免物件建構出來沒怎麼用到就把他dispose()掉。

* 使用Stream串流來讀寫檔，記得要用using把它包起來，若是這個Stream有實作IDisposable介面，當using結束時會自動回收。直接寫的方式要避免，否則會有資源持續占用的Memory leak問題。

### Thread Safe
不要直接開一條Thread，多利用C#的Task
多執行緒需要thread safe
* Dead Lock
* Static
* ConcurrentDictionary

### Race condition
輸出結果由於多執行緒的關係而不一致。

### 例外
try...catch
在編譯完成時，預期會執行到的程式碼會被存放在Cache(讀寫超快)中，但當非預期的例外產生時，需要去Ram(讀寫略慢)中找到例外處理的程式碼來執行，從Ram中拿取程式碼這個步驟很慢。
在迴圈中使用的話，會超慢，原因在於持續載入ram去找非預期的處理程序來做處理。
不要做巢狀try...catch。
catch中不准做商業邏輯，只能做處理例外狀況的處置。
finally只能做清空資源，不准做其他事情。
不要攔截自己不能處理的例外，可以拋出去給別人處理。

### goto
會使程式碼邏輯不清楚，禁用

### doEvent
單緒模擬多緒，禁用


###### tags: `coding style`