# dot net物件導向隨手記
## 友善連結
曹同學筆記c#
https://lavish-neighbor-2f5.notion.site/C-b7951e3fe98d43cfa47e62ac85e7ccec
曹同學筆記SQL
https://lavish-neighbor-2f5.notion.site/2022-12-16-cf153fed8df7420f953297b5f405ec06
曹同學筆記c#
https://lavish-neighbor-2f5.notion.site/2022-12-16-cf153fed8df7420f953297b5f405ec06
郭老師git
gist.github.com/WebappAllenKuo
郭老師電子書
http://www.webapp.com.tw/default.aspx
物件導向心智圖
https://gitmind.com/app/docs/mq5xrh31
c#教學網
https://acupun.site/lecture/csharp/#chp19
## 問體整理
> Q1.單元測試遇到外部資源(時間、資料庫、外部檔案)怎麼辦?
> Q1-1單元測試遇到報表怎麼辦?
> - 把DB資料刪掉,用特定資料塞到特定表單,測完再刪。
> - 外部檔案、時間外部資料盡量不要寫成dll類別庫,無法測試
> - 報表肉眼看出來,不用測。
> Q2.單元測試測試資料太長怎麼辦?
> - 不是壓力測試,不必給極端條件
> Q3.方程式名稱改中文有點怪?
> - 沒關係,中文才看得懂。
> Q4.底線是private的變數慣例?
> - yes,同樣在class的變數名稱,(public)Name,(private)_Name
> Q5.擴充方法不寫成class當公用dll?
> - 擴充方法給單一特定程式使用,可繼承dll再改為特定功能。
> Q6.以ReadBook為例,why擴充方法(字串分段等功能)不寫成class?
> - 架構拆兩層Core(商業核心)和Infra(基礎建設),本題的擴充方法可以寫成Infra的class。
> 參考下圖:
> 
## 程式物件導向重構
### 目的:
可讀性(別人看得懂)、可維護性(別人改得動)、可擴展(低耦合彈性,軟體設計理論,並基於前兩者)
https://dotblogs.com.tw/hochile/2020/02/07/110529
### 程式寫得好定義:
1. 基本60分功能沒問題,需做單元測試!功能與測試可平行開發。
2. 專案、變數、方法命名正確
3. 不要有magic number
4. 思考重用性與不變性,用類別庫-單元測試-主控台開發公用程式,與物件階層。
5. 基於SOLID規則寫程式。
6. 基於Design Pattern
### 程式的重用性
1. 用類別庫-單元測試-主控台應用程式形成
### 變數 & 方法 & 專案命名規則
1. 物件、屬性、或者相關的都是名稱大寫開頭,欄位命名變數,等同變數:習慣小寫命名。
2. 不能有Magic Number
3. 方法定義要做什麼,須明確再Funtion名稱上
4. 專案命名:公司_專案_用途
### 良好的註解習慣
1. 除非命名方法與屬性非常清楚,否則一律加註解。
2. bisnessRule建議一定註解 + 單元測試。
#### 註解範圍用法:
- [1,10] <<< 測1<= x <= 10
- (1,10) <<< 測1< x < 10
### SOLID原則
* 單一職責+開放封閉 => 高內聚+低耦合
* Liskov替換 => 不要亂用繼承,可思考是否用介面替換
* 介面隔離 => 用擴充方法隔開特殊案例,或繼承+介面客製類別。可參考客製化按鈕
* 依賴反轉 => 舉例責任鍊模式可以快速新增或移除規則,在責任鍊上,而非功能或主程式。
以下連結:客製化按鈕
[C#客製化按鈕](https://learn.microsoft.com/zh-tw/dotnet/api/system.windows.forms.ibuttoncontrol)
[物件導向Solid原則](https://skyyen999.gitbooks.io/-study-design-pattern-in-java/content/oodPrinciple.html)
### S: Single responsibility principle(SRP) 單一職責
類別改變的原因只有一個
會計和主管要共用同一個類別,例如打考績(?),不能兩個人共用一個class
### O: Open/close principle(OCP) 開放/封閉原則
開放:可以新增功能 封閉:盡量不要動到原本現有的程式碼
高內聚低耦合
ex:舉例 顧問沒有能力存取你們的程式,只會隨身碟拿來看,告訴你怎麼改。
ex:幫忙上傳東西,結果就故障被罵
ex:顧問一天收費1萬 專業顧問 專業顧門 專業顧門口 名片
### L: Liskov substitution principle(LSP) Liskov替換
邏輯不能錯,正常物件的繼承!
人家的method很好,New 想要的物件搶方法就好,不用繼承他。 郭台銘有錢不用認乾爹直接搶。
汽車會發出聲音,人也會發出聲音。但人不能繼承汽車
### I: Interface Segregation Principle(ISP) 介面隔離
介面有兩個class用他
但偷懶 介面實際只有共用兩個,但寫成8個,某一個類別用不到8個,其他6個丟出例外,這種是不行的。
解決方案:發現不是每個底下類別都需要這個介面,
ex:門的介面有可以鎖的方法,但廚房的砂門沒有鎖,所以不能共用門的介面,要自己再開一個
### D: Dependency Inversion Principle(DIP) 依賴反轉
習慣用高階的呼叫低階的功能。ex購物網站的購物功能,要叫底下的會員功能a,但某天有人買購物功能,需要呼叫另外的會員系統功能b。
要新增的話改會員功能,就不用跟也要同時改購物的bug再一起

### Design Parrtern
書:針對23種問題的解法((所以沒有MVC
不容易應用書上的範例到實際程式碼
上班公司的東西都做得出來。那就該學了(約一年多)。
- 案子功能交付後,用現成案子去試designPattern。
- 一個類別可以實做很多個介面。
## 基礎1-1: 物件導向知識學習
### 欄位field vs 屬性property差異
1. 欄位field 外界可以存取(無法驗證),屬性property數值進來前可以驗。
2. 屬性property可以做為read only或write only。
### 建構子
###### 快捷鍵
prop+tab+tab 建構屬性
ctor+tab+tab 建構子
變數var 幫你猜變數型態
若無建構函數,物件會幫你建立空白建構子。
- 下圖:若有建構子,若有參數,建構時需引入,否則物件不能new一個建構子。

- 建構子要設定參數嗎?看情況,以下範例:
###### 範例
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
internal class Program
{
static void Main(string[] args)
{
//參數要寫在constructor or method?
// 定義參數
string path = @"d:\temp\demo.txt";
string path2 = @"d:\temp\demo2.txt";
string content = "Hello,Allen";
string content2 = "Hello,Simon";
//參數要寫在constructor:不佳,若要換路徑刪檔案就不方便了
new FileHelper(path).Save(content);
new FileHelper(path2).Save(content2);
//參數要寫在method:good
var helper = new FileHelperB();
helper.Save(path, content);
helper.Save(path2, content2);
}
}
public class FileHelper
{
public FileHelper(string fileName)
{
FileName = fileName;
}
public string FileName { get; }
/// <summary>
/// 將內容存檔
/// </summary>
/// <param name="content"></param>
public void Save(string content)
{
}
}
public class FileHelperB
{
public FileHelperB()
{ }
/// <summary>
/// 將內容存檔
/// </summary>
/// <param name="path"></param>
/// <param name="content"></param>
public void Save(string path, string content)
{
}
}
}
```
#### 所有class要用到的資訊,就寫在建構函數
- 下圖:建構子是需要初始化的值,但不是所有參數,都需要先存到建構子,例如檔案存讀檔,檔案路徑就不用先存到建構屬性

### 擴充方法
#### 擴充方法是甚麼
針對內建的類別新增一個方法,原本的程式資料型態底下可用的方法都定義好了,為了客製某些功能,就用擴充方法
#### 擴充方法不寫在公用utility
針對utility要所有的人都更新,要是更新有bug,如同window更新
#### 實作:擴充方法
針對int的型別擴充
1. class 寫static
2. method寫static
3. 參數第一個加this
- 下圖:擴充方法操作
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
int price = 210;
// int tax = Order.CalcTax(price);
int tax = price.CalcTax();
Console.WriteLine(tax);
string value = "0123456789";
string result = value.Left(4);
Console.WriteLine(result);
}
}
public static class Order
{
public static int CalcTax(this int price)
=> (int)Math.Round(price * 0.05, MidpointRounding.AwayFromZero);
public static string Left(this string value, int length)
{
return value.Substring(0, length);
}
}
}
```
### 委派
委派: event, LINQ, Functional Programing(FP)
#### 1.委派的宣告
簽名碼、語法糖
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
// 簽名碼: OperationHandler(int num1, int num2);
public delegate int OperationHandler(int num1, int num2);
internal class Program
{
static void Main(string[] args)
{
// 語法糖:;
// OperationHandler handler = new OperationHandler(Add);
OperationHandler handler = Add;
int result = handler(10, 4); //2
}
static int Add(int n1, int n2)
{
return n1 + n2;
}
}
}
```
#### 2.委派替換方程式
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
internal class Program
{
static void Main(string[] args)
{
int price = 20000;
int total = new Order().CalcPromoPrice(price, CalcByTotal);
Console.WriteLine(total);
}
static int CalcByTotal(int price)
=> price >= 10000 ? price - 1000 : price;
static int CalcByDiscount(int price)
=> price * 8 / 10; //八折
}
public delegate int PromoHandler(int price);
public class Order
{
/// <summary>
/// 計算優惠價
/// </summary>
/// <param name="price">原始售價</param>
/// <returns></returns>
public int CalcPromoPrice(int price, PromoHandler handler)
{
return handler(price);
}
}
}
```
#### 3.委派替換匿名函式
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp6
{
internal class Program
{
static void Main(string[] args)
{
OperationHandler addFunc = delegate(int num1, int num2){ return num1 + num2; };
addFunc = (int num1, int num2) => num1 + num2;
addFunc = (num1, num2) => num1 + num2;
int result = addFunc(1, 5); // 6
Console.WriteLine(result);
TruncateHandler handler = (string value) => value.Substring(0, 3);
handler = (value) => value.Substring(0, 3);
handler = value => value.Substring(0, 3);
}
}
public delegate string TruncateHandler(string value);
public delegate int OperationHandler(int num1, int num2);
}
```
#### 4.委派之匿名函式Func<>
前一個return,後兩個參數
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp7
{
internal class Program
{
static void Main(string[] args)
{
Func<int, int, int> addFunc = Add;
Func<string, int, string> leftFunc = Left;
Func<int, string, DateTime, int, double, string> func = xxx;
Action greet = Greet;
Action<int> funcDelete = DeleteMember;
}
static void Greet()
{
Console.WriteLine("Hello, world");
}
static void DeleteMember(int memberId)
{
}
static string xxx(int n1, string v1, DateTime d1, int n2, double d2)
{
return "AAA";
}
static string Left(string value, int lenght)
{
return value.Substring(0, lenght);
}
static int Add(int num1, int num2)
{
return num1 + num2;
}
}
}
```
### 4.1微軟範例
```csharp=
// Declare a Func variable and assign a lambda expression to the
// variable. The method takes a string and converts it to uppercase.
Func<string, string> selector = str => str.ToUpper();
// Create an array of strings.
string[] words = { "orange", "apple", "Article", "elephant" };
// Query the array and select strings according to the selector method.
IEnumerable<String> aWords = words.Select(selector);
// Output the results to the console.
foreach (String word in aWords)
Console.WriteLine(word);
/*
This code example produces the following output:
ORANGE
APPLE
ARTICLE
ELEPHANT
*/
```
### 4.2 老師範例
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp8
{
internal class Program
{
static void Main(string[] args)
{
int[] items = new int[] { 1,2,3,4,5,16,7,38,9,10};
// Func<int, bool> func = number => number % 2 == 0;
Func<int, bool> func = number => number % 2 == 1;
List<int> result = items.MyFilter(func);
Console.WriteLine(result);
Console.ReadLine();
}
}
public static class ListExts {
public static List<int> MyFilter(this int[] source, Func<int, bool> func)
{
List<int> evenItems = new List<int>();
foreach (var item in source)
{
if (func(item) == true) evenItems.Add(item);
}
return evenItems;
}
}
}
```
#### 5.委派替換方程式: 應用在畫三角形上
- 主程式
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine(Triangle.Draw(5, Triangle.Left));
Console.WriteLine();
Console.WriteLine(Triangle.Draw(5, Triangle.Right));
Console.WriteLine();
Console.WriteLine(Triangle.Draw(5, Triangle.Center));
Console.WriteLine();
}
}
}
```
- 類別庫
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
public delegate string RowHandler(int rows, int rowIndex);
public class Triangle
{
private static string Draw_Right(int rows, int rowIndex)
=>new string('*', rowIndex + 1).PadLeft(rows, ' ');
private static string Draw_Left(int rows, int rowIndex)
=> new string('*', rowIndex + 1);
private static string Draw_Center(int rows, int rowIndex)
=> new string(' ', rows - rowIndex - 1) + new string('*', (rowIndex + 1) * 2 - 1);
public static RowHandler Left
{
get { return Draw_Left; }
}
public static RowHandler Right=>Draw_Left;
public static RowHandler Center => Draw_Center;
public static string Draw(int rows, RowHandler handler)
{
string result = string.Empty;
for (int i = 0; i < rows; i++)
{
result += handler(rows, i) + "\r\n";
}
return result;
}
}
}
```
### 介面Interface
#### 定義介面

- 下圖: 介面有的,class一定要有,並且是public

### 繼承
- 下圖: 沒有繼承就是繼承object(所以任何的物件都有方法ToString, GetType),就算是struct的父類別是valueObject,valueObject的父類別是object

- 下圖: class 繼承(inherit)父類別,class實作(implement)介面

- 下圖: 可以快速用同樣的create的方法,確保裡面有相關屬性可以用

- 下圖: 輸出輸入可以降規或者升規

### override
在子類別與父類別資料中,分出是子類別,或是父類別
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp5
{
internal class Program
{
static void Main(string[] args)
{
//Employee emp = new Employee();
//Console.WriteLine(emp.GetTitle());
//Sales emp = new Sales();
//Console.WriteLine(emp.GetTitle());
Employee[] employees = {
new Employee(),
new Sales()
};
foreach (Employee employee in employees)
{
Console.WriteLine(employee.GetTitle());
}
}
}
public class Employee
{
public virtual string GetTitle()
=> "Employee";
}
public class Sales:Employee
{
public override string GetTitle()
=> "Sales";
}
}
```
### Override& 委派事件
- class寫值觸發事件:委派事件寫法順序
定義委派事件要包含傳: 1. object 2. 自定義資料形式
寫入類別庫class屬性時,set內寫執行類別庫class的方法
類別庫class的方法,「因為主程式的方法委派給類別庫方法」,所以能觸發主程式class的方法,並執行。
- 子類別方法覆寫父類別方法,
protected virtual(父類別方法),
protected override(子類別方法),
```csharp=
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp4
{
internal class Program
{
static void Main(string[] args)
{
Member member = new Member();
member.DataChanged += Member_DataChanged;
member.Name = "Allen";
Member member2 = new Member();
member2.DataChanged += Member_DataChanged;
member2.Name = "Simon";
}
private static void Member_DataChanged(object sender, DateTime dt)
{
Member member = sender as Member;
Console.WriteLine("事件被觸發了,Name=" + member.Name + " @" + dt);
}
}
public delegate void DataChangedEventHandler(object sender, DateTime dt);
public class Member
{
public event DataChangedEventHandler DataChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception("Name不能是null");
if (value.Length > 30) throw new Exception("Name長度不能超過30");
_name = value;
OnDataChanged(DateTime.Now);
}
}
protected virtual void OnDataChanged(DateTime arg)
{
// raise event
if (DataChanged != null) DataChanged(this, arg);
}
}
public class VIP : Member
{
private string _email;
public string Email
{
get { return _email; }
set
{
_email = value;
// raise event
OnDataChanged(DateTime.Now);
}
}
protected override void OnDataChanged(DateTime arg)
{
// base.OnDataChanged();
}
}
}
```
### Override(覆寫) & Overload(多載) &Polymorphism(多型)的差異
覆寫(Override)是指子類別可以覆寫父類別的方法內容,使該方法擁有不同於父類別的行為。
多載(Overload)指在一個類別(class)中,定義多個名稱相同,但參數(Parameter)不同的方法(Method)。
多型(Polymorphism)是指父類別可透過子類別衍伸成多種型態,而父類別為子類別的通用型態,再透過子類別可覆寫父類別的方法來達到多型的效果,也就是同樣的方法名稱會有多種行為。
## 基礎1-2: 類別庫
狀況: 若有一個程式,例如股票市場營業時間true or false,若有6個地方需要同一個程式,程式1貼到程式2還好,但程式2複製貼上又改了,然後有bug,程式3又需要這份code,試問要程式3要複製程式2還是程式1呢?
建議:class公用寫在*.dll檔案。
- [x] - 類別庫(*dll)的版本越低越好
### 加入dll參考
放在bin的debug或release就可以了
建置方案就能立刻參考dll的動作
### 註解引入
先在原方案(方案右鍵屬性> 建置)產生XML檔案,再到參考dll的程式(右鍵建置),即可引入

### *.dll程式庫版本控管
#### 命名習慣
更新版本的原則,前兩個版本一樣。
1.0.0
1.0.15
- 若有bug要更新,更新最後一個小版本!
- 若有物件(功能)新增,更新中型的版本!
- 若有.net的版本要更新或者method的版本要更新,更新大型版本!
.net版本不同範例:
字串dollar sign@ stringformat$"" << .net 4.8才有
定義小版本更新bug小。
定義中版本更新後沒有用到,給客戶虛榮心提升。
定義大版本,軟體版本號不能更新。
- 其他:召回更新.dll,不要更新就好Ans:很難,別人會互相比較。
#### 實作:更新dll版本
- 到Property的Assenbly

## 基礎1-3: 單元測試
### 專案命名規則
命名規則如下:
> 公司名.projectName.用途
開空白方案:.net framework
namespace ispan_praticeDotNet_practice
### 任何c#可以用/// 三條橫線加註解!
註解可以用xml檔註記
### 單元測試
#### 優點
強迫自己寫易測試&優質的程式碼
開發時可以重覆測試減少開發時間
別人快速懂功能
重構程式的保護
#### 測試用邊界條件
詳情看12/15下午剛開始
#### 時間區間題目檢討:老師的版本
可以買程式重構的書來看!
- 下圖:老師的解答1

- 下圖:老師的解答2

## 範例1. 電子書範例
### 1. 開專案
Ispan.ReadBooks.winform (.NetFramework4.8)
Ispan.ReadBooks (.NetFramework類別庫4.8)
Ispan.ReadBooks.UnitTests
ctrl + shift + B = 快速建置方案的快捷鍵
- 下圖:程式碼供參,每個測試記得在上面加入[Test]

- 下圖:程式碼要資料夾 & class名稱要對稱

### 2. 需要FUNCTION<<<用擴充方法(第一個功能)
page = p.GetSubArray(4,6)
content = page.concat(enter)
### 3. 單元測試情境(第一個功能)
### 4. 調整參數順序ValueTuple(第二個功能)
- 下圖:程式碼供參 for調整參數順序(類別庫功能)
```csharp=
public static class ValueTupleExtension
{
/// <summary>
/// 確保ValueTuple的值有依照大小順序
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static (int, int) EnsureInOrinal(this (int begin, int end) source)
{
// 古時候用ref,現在比較新的版本可以用valueTuple調整參數順序
// (int begin, int end) <<< 這個是資料型別Tuple
// source 等同 (source.begin, source.end)
return source.begin <= source.end ?
source :
(source.end, source.begin);
}
}
```
- 下圖:程式碼供參 for調整參數順序(單元測試)
```csharp=
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ispan.ReadBooks.Extensions;
namespace Ispan.ReadBooks.UnitTests.Extensions
{
internal class ValueTupleExtension
{
[Test]
public void Ensure_順序正確_傳回原本順序()
{
(int, int) soure = (4, 6);
(int, int) expected = (4, 6);
(int, int) actual = soure.EnsureInOrinal();
Assert.AreEqual(expected, actual);
}
[Test]
public void Ensure_順序相反_傳回相反順序()
{
(int, int) soure = (14, 6);
(int, int) expected = (6, 14);
(int, int) actual = soure.EnsureInOrinal();
Assert.AreEqual(expected, actual);
}
}
}
```
- 下圖:用TestCase的attribute特徵項的用法,程式碼供參 for調整參數順序(單元測試)

### 5. 取出陣列中的字串(第3個功能)
```csharp=
public static string[] GetSubArray(this string[] source, int beginIndex, int endIndex)
{
if (source == null || source.Length == 0) return Array.Empty<string>();
beginIndex = beginIndex.EnsureinRange(0, source.Length - 1);
endIndex = endIndex.EnsureinRange(0, source.Length - 1);
//若大小相反,就對調
(int Begin, int End) range = (beginIndex, endIndex).EnsureInOrinal();
beginIndex = range.Begin;
endIndex = range.End;
int length = endIndex - beginIndex + 1;
string[] destination = new string[length];
Array.Copy(source, beginIndex, destination, 0, length);
return destination;
}
```
- Array.Copy的參數

- 改成泛型方法

### 6. 取Array每個element TO string功能(第四個功能)
- 聚合函數Aggregate
```csharp=
public static string Concat(this string[] source, string seperator)
{
if (source == null || source.Length == 0) return string.Empty;
return source.Aggregate((acc, next) => acc + seperator + next);
// stringArr.Concat(;)
// 輸入stringArr = ["a","b","c"]
// 輸出 a;b;c
}
```
### 7.為何用this
1. 偷懶,自己呼叫自己。不用打一長串的自己值
2. 區分兩個同名變數,是this還是傳入值
## 範例2. YCD(香腸遊戲) DiceGame

### 0. 特殊語法筆記
#### 0.1 建構子被呼叫前的建構。用以區隔單元測試與實際函數
```csharp=
public YcdDiceGame() : this(new DefaultRandomValueProvider())
{
}
public YcdDiceGame(IRandomValueProvider provider)
{
// pre conditions
if (provider == null) throw new ArgumentNullException(nameof(provider));
do
{
dices = DiceUtility.CreateDices(provider, 4);
} while (DiceUtility.AreUnique(dices) == true);
}
```
程式碼中的 : this(new DefaultRandomValueProvider()) 是一個建構子的呼叫語法,意思是在建立新的 YcdDiceGame 物件時,會先呼叫另一個建構子,並傳入一個新建立的 DefaultRandomValueProvider 物件。這個建構子的作用是用來初始化新建立的 YcdDiceGame 物件,並且會在執行完這個建構子之後再執行這個建構子(也就是 public YcdDiceGame() : this(new DefaultRandomValueProvider()))。
#### 介面替換的方法
```csharp=
public MockRandomValueProvider(int value, params int[] others)
{
_numbers.Add(value);
_numbers.AddRange(others);
}
```
可以用 params傳入多個
- 下圖: 前面數值大 比較結果return 正數

int有實作IcompareTO,所以一定有compareTo
#### 寫亂數seed的用法
- 下圖: 寫亂數seed的用法

- 下圖: Tab + Tab <= 委派c# 自動產生方法

- 子類別無法呼叫父類別的事件,除非Virtual
On+事件名稱 <= 命名委派名稱
Virtual <= 子類別有機會override改寫

- 事件要帶的參數


## 範例3:責任鍊

## C#程式擴充工具
### ReSharper
可以幫忙引入using,加入程式開發
### 神秘軟體可以讓行政人員編輯前端
c#額外付出費用?
## 面試
// 會不會interface
引言:8萬應徵5萬的司機,第一句問什麼?有駕照。
一般面試公司篩選人,.net也是。
老師的問法:會不會interface?
// 唬人小故事
組員買消夜給其他人,面試時沒有你其他組員會QQ,餓死
### 面試放作品
我的作品有這個,特色,我用了甚麼技巧。
遇到甚麼困難,怎麼解決。
無腦放作品不會有人想點連結。
### 面試題
#### 面試題:1+2+3+...+n=用梯形公式算
#### 面試題:left outer join + group by
#### 面試題:取商數
a = 10/4 <<< a = 2 因為10和4都是整數,所以出來也是整數。
a =10.0/4 or a = 10/4.0 <<< 2.5
#### 面試題:空字串的3種用法


```csharp=
// 1樓比較好 因為每次都找同一個空字串位址
// 2樓每次都宣告一個空位址
string a = string.Empty
string b = "";
```
## 其他小知識
### 維護合約知識
總共10萬
1/5 簽約3萬
2/5 交付4萬
2/12 第二次交付
2/22 客戶驗收3萬,到明年2/22月前是保固期
- 若20年後要問問題,把20年的保固錢一次收齊。
- 維護時間點 若客戶到硬撐到最後一刻才肯維護費 週期照驗收那樣算的。
- 改一個文字的故事,每次改少少的,收錢5000被嫌,下次4萬。
### 看螢幕時
眼睛要對齊螢幕上緣。頸椎壞掉56萬。
### 故事
// 客戶買了不用
button沒有統一放在下面。
不知道網站可以放大字形,所以button跑到畫面外。
// 唬人小故事DesignPattern入門先看簡單再看難
人死掉,天使帶他去天堂&地獄
天堂:餵鴿子
地獄:沙灘排球比基尼
### 併購故事
Lotus 123 <<< excel 影片支援,IBM買Lotus丟掉123
Access <<< 買對方的軟體,讓他不要更新
Am: Pro <<<word office裝起來就當機
c# 可以生成ios和android的app <<< 一套200萬沒人買,直接買下這家公司。
Github <<< 買下來做azure devops?
skype <<< 買下來用MSN Messenger要轉skype 聊天紀錄 自訂人名都不見。
### 換字體注意
i I l 1
B 8
0 o O
### 網路問題
網路問問題禮貌:問完不能刪
網路上回答問題:有高手會開書單
---
###### tags: `c#筆記`