數(shù)據(jù)類型
具有某種數(shù)據(jù)類型的數(shù)據(jù),稱為這個數(shù)據(jù)類型的實例。如學(xué)生是類型,王小二是實例。
類型可以擁有自己的成員(屬性和方法等)。 類型type指的是類、結(jié)構(gòu)、接口、委托、枚舉。
C#的類型包括值類型和引用類型,其中重要的類型定義成基元類型。
C#的預(yù)定義類型:8種整數(shù)型,2種2進質(zhì)浮點型,1種金融十進制浮點型,1種布爾型,1種字符型。
- 值類型 堆
int long char double datetime 屬于結(jié)構(gòu) 枚舉emnu
- 引用類型 棧
string class 接口 委托
引用類型只可以為null,值類型不可以
string str=null;
string str1='//n';//轉(zhuǎn)義
類實際對象的計算機語言表示,擁有許多成員,最重要的是屬性和方法。
對象是類的實例
屬性存儲信息和數(shù)據(jù)
方法是供我們可用的函數(shù)
運算符和分支結(jié)構(gòu)
- 運算符
//一元運算符
+ - ++ --
int i=i++ i=i-- //放后是執(zhí)行完再加一
//二元運算符
+ - * / %
== != < > <= >= //關(guān)系運算
&& ||
+= -= *= /= %=
//三元運算符
? :
//運算符重載
//分支結(jié)構(gòu)
if else
switch case
方法與參數(shù)
方法method
用來把一些相關(guān)的語句收集在一起的代碼塊。方法必須有方法名,擁有0個或多個參數(shù),可以有0或1個輸出,C#7后可以有多個輸出,在這之前借助Out關(guān)鍵字實現(xiàn)多輸出。
一般定義不返回函數(shù)值得叫方法,返回值的叫函數(shù)。
C#所有數(shù)據(jù)結(jié)構(gòu):類 結(jié)構(gòu) 接口,可以有方法。 枚舉 不可以寫方法,委托的方法是固定的不可以自定義
方法的定義/簽名signature
- 修飾詞 public protected internal private
- 方法的返回類型。void
- 方法名稱
- 方法參數(shù)
- 實際參數(shù)和形式參數(shù)
- 按值傳遞和按引用傳遞ref
- out關(guān)鍵字的使用
- 可變關(guān)鍵字 params 數(shù)組傳參 必須在最后
//定義
public int a(params int[] b){
return 0;
}
//調(diào)用
a(1,2,3)//可變長度
- 可選參數(shù)
- 命名參數(shù)
- 遞歸調(diào)用recursion
數(shù)組與循環(huán)
- Array a =new int[10]
- 索引 從0開始 索引循環(huán)的類型是一致的
- 一維數(shù)組
- 二維數(shù)組 交錯/齒輪數(shù)組
- 循環(huán) for foreach
- while和do while
面向?qū)ο蠛皖?/h2>
- 面向?qū)ο?object oriented
- 封裝集成多態(tài)
- 類里包括:
常量const、字段field、屬性property、構(gòu)造函數(shù)、析構(gòu)函數(shù)、自定義方法、嵌套類
- 構(gòu)造函數(shù) construct
當(dāng)使用new關(guān)鍵字時,自動調(diào)用構(gòu)造函數(shù),構(gòu)造函數(shù)名稱與類相同,public,無返回值。構(gòu)造函數(shù)可以是private(單例模式)
public class car{
public colors color{
get{
return this;
}
//set打開后,get也要打開
};
set{
this=value;//默認(rèn)value
};
}
}
- 對象初始化器
- 靜態(tài)成員
靜態(tài)方法只能使用靜態(tài)字段和屬性。
靜態(tài)成員無需實例化即可使用。
靜態(tài)成員屬于類而非類的實例,但被所有類共享。
實例的方法可以使用靜態(tài)和實例字段和屬性。
- 靜態(tài)構(gòu)造函數(shù)
當(dāng)你的類中有靜態(tài)函數(shù),C#默認(rèn)會創(chuàng)建一個靜態(tài)構(gòu)造函數(shù),為你的靜態(tài)成員賦值,不能有修飾詞,不能有輸入和輸出。靜態(tài)構(gòu)造函數(shù)只調(diào)用一次。
結(jié)構(gòu)
- 結(jié)構(gòu)是值類型,可以被看作是輕量級的類
- 設(shè)計結(jié)構(gòu)是為了提升程序的性能:
引用用類型內(nèi)存的初始化需要牽扯到堆上內(nèi)存的分配,因此如果一個自定義的類型==只會包括值類型作為成員==,使用結(jié)構(gòu)是一個較好的選擇
- C#自帶的結(jié)構(gòu)有很多,包括int,DateTime等,它們的成員==全部是值類型==。
- 結(jié)構(gòu)可以有自己的方法
- 結(jié)構(gòu)的成員最好是值類型
繼承inheritance
- 所有類繼承于System.Object Get.Type(反射)
- private 只用自己能訪問
- protected 自已和子類
- this當(dāng)前類型,可以用this呼叫其它構(gòu)造函數(shù),Base調(diào)用基類構(gòu)造函數(shù)
- 實例化對象會一層一層,執(zhí)行到System.Object基類
- 方法的重寫override、重載overload、隱藏
- 方法隱藏,子類和父類方法名稱相同,自動隱藏,子類可以用new關(guān)鍵字
- 方法重寫,子類和父類擁有相同的簽名方法,但子類希望得到不同的行為,而且父類的方法是虛方法或抽象方法。
- 方法重載,子類和父類有相同的方法名稱,但簽名不同。
- 密封類sealed:
- sealed修飾類型,不能補繼承
- sealed修飾方法,不能被重寫
- 結(jié)構(gòu)是密封類型,不能被繼承
- 抽象類abstract:必須被繼承的類
- 無法實例化一個abstract,子類必須用override來重寫它
- 不能與sealed修飾
- 比較虛的概念使用abstract
- abstract可以有普通的方法,也可以有抽象方法
- 接口-特殊的抽象類,只能有抽象方法
- 抽象方法
- 抽象方法本身沒有方法體,待子類去實現(xiàn)
- 抽象方法必須在抽象類中
- 虛方法virtual
- virtual可以有方法實體,也可以沒有
- 子類可以重寫,也可以不重寫
- 用于父類可以實現(xiàn),但子類有不同實現(xiàn)方式的情景
public class a{
int x;
int y;
public a(){}
public a(int x,int y){
this.x=x;
this.y=y;
}
}
public class b:a{
public b(int x,int y):base(x,y){}
}
多態(tài)和接口
- 多態(tài) polymorphims
- 一個類型有多種狀態(tài)。
- 接口 interface
- 接口是一種特殊的類,方法只能有簽名,而不能有方法體。
- 使用借口實現(xiàn)has-a關(guān)系。
- 使用接口實現(xiàn)多重繼承,解決單一繼承的缺陷。
- 接口就像技能,通過多重繼承賦予不同的技能。
- 策略模式
- ==依賴注入==
第三部分 數(shù)據(jù)結(jié)構(gòu)
- C# 2.0 泛型
- C# 3.0 linq
- C# 4.0 動態(tài)語言
集和與泛型
- 數(shù)據(jù)結(jié)構(gòu) Data Sturcture
- 數(shù)據(jù)結(jié)構(gòu)是以某種形式將數(shù)據(jù)組織在一起的集合,它不僅存儲數(shù)據(jù),還支持訪問和處理數(shù)據(jù)的操作。
- 簡單分類:線性表,鏈表,哈希表,樹,圖等
- 大部分編程語言都可以實現(xiàn)這些數(shù)據(jù)結(jié)構(gòu),C#已經(jīng)為我們實現(xiàn)了一部分,例如線性表(Array,List,ArrayList,Stack,Queue),鏈表(LinkedList),哈希表(HashTable,Dictionary)等等
- C#中的集合對應(yīng)了一些數(shù)據(jù)結(jié)構(gòu)
- 集合 collection
- 包括泛型集合與非泛型集合,它們都是不定長的
- 非泛型集合:集合里的元素可以為任意的類型
- 泛型集合:集合里的元素只可以為一種類型T
- 從定義上,數(shù)組(Array)不是集合因為它是定長的
- 非泛型集合
- ArrayList
- Stack 棧
- 后進先出(LIFO)
- 棧是限制插入和刪除只能在一個位置上進行的線性表,該位置是表的末端,叫作棧頂,對棧的基本操作有push(進棧)和pop(出棧),前者相當(dāng)于插入,后者相當(dāng)于刪除最后一個元素
- 練習(xí):棧中全是int,找出棧中最小的元素
- Queue 隊列
- 先進先出(FIFO)
- 隊列是一種特殊的線性表,特殊之處在于它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。
- 裝箱boxing與拆箱unboxing
-非泛型集合的兩個問題:造成裝箱和拆箱\類型不安全
- 裝箱的定義:將值類型分配給object變量
- 拆箱的定義:將object變量中的值賦給一個值類型
- 拆箱有風(fēng)險,操作需謹(jǐn)慎
- 泛型集合
- 泛型集合List<T>
- T稱為類型參數(shù)或占位符
- 實際使用時必須為T指定一個具體類型,例如int,string等,稱為實際參數(shù)
- 棧和隊列的泛型版本
- 由于性能和類型安全,出現(xiàn)泛型后以經(jīng)沒有人使用非泛型合集
- 泛型方法
- 泛型方法:輸入輸出參數(shù)包括了類型參數(shù)
- 例如:交換任意兩個同類型變量的值
- 使用泛型方法可以達到代碼復(fù)用的目的
- 泛型類型
- 可以自定義泛型結(jié)構(gòu)和類
- 例如,創(chuàng)建一個自定義的泛型結(jié)構(gòu)
- 泛型約束
- 泛型約束使得類型參數(shù)T只能是滿足某種條件的類型
- 接口約束:使得泛型的實際參數(shù)必須實現(xiàn)某個接口
- 基類型約束:實參必須是某個類的派生類
- 構(gòu)造函數(shù)約束:泛型實參必須具有可訪問的無參構(gòu)造函數(shù)(默認(rèn)的也可)
public a<T,K>(T b,K c) where T: new() where K:struct
- 可空類型
- 定義為Nullable<T> 或者用?
- 一個結(jié)構(gòu),使用HasValue()判斷是否有值
算法介紹
- 算法 algorithm
- 定義 有限步操作的集合
- 有線性、確切性、輸入、輸出、可行性
- 時間復(fù)雜度O(S)和空間復(fù)雜度n(S)
-
- 常用數(shù)據(jù)結(jié)構(gòu)
- 線性列表
- array 定長 類型安全
- arrayList 非泛型 數(shù)據(jù)類型不安全
- List<T> 泛型
- 鏈表
- 定義:包括節(jié)點和下一個借點的引用。在內(nèi)存中不連續(xù)。
- 雙向鏈表則還包括前一個節(jié)點的引用。
- 好處:節(jié)省內(nèi)存空間,中間插入和刪除的時間復(fù)雜度底。(常數(shù)復(fù)雜度,線性列表為線性)
- 壞處:不能按索引器訪問
-哈希表 hashtable
-哈希表是鍵值對數(shù)據(jù)結(jié)構(gòu)key value
-哈希函數(shù)和哈希碰撞(解決哈希碰撞,線性尋址法,平方尋址法)
- 插入和刪除的時間復(fù)雜度都是常數(shù),但哈希表不能排序。
- 節(jié)省空間,速度快
- hashtable非泛型
- Dictionary<K,T>
-
CLR(公共語言運行時)
- .net框架主要成員
- 兩部編譯和跨平臺
-中間語言IL,c#通過csc.EXE,先編譯為中間語言,在運行時,CLR通過JIT即使編譯為機器碼。
委托和事件
-
委托 delegate
- 委托是一個特殊的類,是密封袋,不可以繼承。包括一個特殊的方法invoke,也可以看成一個簽名。
- 使用委托傳入方法,起到代碼復(fù)用的目的
- 可以為委托提供特殊的簽名方法
-
委托的原理:
- 委托是一個密封類,它繼承自System.MulticastDelegate,后者再繼承自System.Delegate
這個類的成員是固定的,這個密封類包括一個構(gòu)造函數(shù)和三個核心函數(shù),Invoke方法賦予其同步訪問的能力,BeginInvoke,EndInvoke賦予其異步訪問的能力
- 委托鏈:
- System.MulticastDelegate是所有委托的父類,它含有一個非常重要的字段_invocationList,指的是委托自身的方法鏈,它是一個Delegate類型的數(shù)組,所以委托可以掛接多于一個函數(shù)(即一個函數(shù)List),可以為這個鏈自由的添加或刪除函數(shù)。
- 一個委托鏈可以沒有函數(shù)如果執(zhí)行委托,將會順序的執(zhí)行委托鏈上所有的函數(shù)。如果某個函數(shù)出現(xiàn)了異常,則其后所有的函數(shù)都不會執(zhí)行如果你的委托的委托鏈含有很多委托的話,你只會收到最后一個含有返回值的委托的返回值
- 委托鏈?zhǔn)褂?=將方法鏈接。
- System.Delegate是System.MulticastDelegate的父類,它含有2個非常重要的字段_target和_methodPtr。如果委托指向了一個實例方法,_target將會等于該實例本身。在方法中,可以通過this訪問。如果委托指向了一個靜態(tài)方法,_target將會等于null無論委托指向什么方法,_methodPtr都是CLR用于標(biāo)示委托指向方法的IntPtr值
- 委托的構(gòu)造函數(shù)設(shè)置_invocationList,_target和_methodPtr在進行調(diào)用時,Invoke方法使用_target和_methodPtr在指定對象上調(diào)用對應(yīng)的方法
-
事件
- 事件必須包括三部分:
- 事件的訂閱者(Subscriber),訂閱者會在事件觸發(fā)之后,做出響應(yīng)行為。
- 事件的觸發(fā)者,或者發(fā)布者(Publisher),條件得到滿足時,觸發(fā)事件訂閱者和觸發(fā)者之間的數(shù)據(jù)傳送通道。
- 事件使用步驟
- 聲明一個事件處理方法,指出事件發(fā)生時,訂閱者應(yīng)該有怎樣的反應(yīng)。該方法可以傳入繼承自EventArgs類型的自定義數(shù)據(jù)
- 聲明委托。委托的簽名和事件處理方法相同
- 聲明基于該委托的事件
- 為事件增加訂閱者
- 在事件符合條件時,調(diào)用事件
- 事件本身是私有的
- 事件會被轉(zhuǎn)化為一個私有的字段,以及兩個方法。字段的類型和事件關(guān)聯(lián)的委托相同,而兩個方法的輸入類型和事件關(guān)聯(lián)的委托相同,沒有輸出
- 事件將字段改為私有,從而令外部不能更改它的值
- 在事件所處的對象之外,事件只能出現(xiàn)在+=,-=的左方
-
泛型委托
- C# 2 中提供了兩個泛型委托Action和Func
- Action是沒有輸出的,F(xiàn)unc可以有一個輸出
- 使用它們可以代替原始的delegate關(guān)鍵字
委托實際上就是函數(shù)指針,將不同函數(shù)像變量一樣傳遞,供不同參數(shù)調(diào)用。
匿名類型、var關(guān)鍵字和擴展方法
- 隱式類型
- 使用var關(guān)鍵字
- 只能對局部變量使用隱式類型
- 在foreach中一般不需要指定類型
- 匿名類型
- 匿名類型允許你直接在花括號內(nèi)建立類型,而無需指定類名稱
- 只能用var或object來修飾,用var修飾可以用點來獲取成員
- 如果兩個匿名類型有相同的成員,且成員類型相同,順序相同,編譯器會將它們做為同一個類型。
- 擴展方法
- 在不創(chuàng)建子類,不更改類型本身的情況下,修改類型
- 擴展方法必須定義于靜態(tài)的類型中,且擴展方法必須是靜態(tài)的。
- 擴展方法第一個輸入?yún)?shù)要加上this,擴展方法必須至少有一個輸入?yún)?shù)
//匿名類型
var s = new { name = "小李", age = 15 };
var b=s.name;
Console.WriteLine(s.GetType());//返回<>f__AnonymousType0`2[System.String,System.Int32]
//擴展方法
public static class StringExtension //1、在靜態(tài)類中,2、一般用Extension做為后綴
{
public static bool isHuiwen(this string input)
{
//算法
return true;
}
}
bool a = "string".isHuiwen//調(diào)用
匿名方法和lambda表達式
- 匿名方法出現(xiàn)在C#2中
- 閉包:可以使用捕獲變量(在匿名方法外部的變量)
- lambda表達式
- C#3為了令代碼更加簡化,引入了lambda表達式
- lambda表達式,是表達式的一種,它可以表式一個函數(shù)
- 基本結(jié)構(gòu)分三個部分
- 輸入?yún)?shù) 一個參數(shù)不需要括號,多個參數(shù)或沒有參數(shù)需要括號
- 箭頭 =>
- 表達式或語名塊 如有只一條件語句不需要花括號,多行需要花括號
//old:泛型委托實現(xiàn)字符串判定
static void Main(string[] args)
{
var data = new List<string> { "string", "this", "the", "a", "dog" };
var func = new Func<string, bool>(Predicate);//泛型委托實現(xiàn),string輸入的參數(shù),bool返回的類型
//使用匿名方法的方式,不用在單獨定義Predicate這個方法
var func1 = new Func<string, bool>(delegate (string input)
{
return input.Length >= 4;
});
//lamdbe表達式寫法
Func<string, bool> func2 =(input)=>{
return input.Length >= 4;
};
foreach (var word in data)
{
if (func(word))
{
Console.WriteLine(word);
}
}
Console.ReadKey();
}
public static bool Predicate(string input)//使用匿名方法就這用寫這個了
{
return input.Length >= 4;
}
LinQ Language-Integrated Query語言結(jié)構(gòu)化查詢
-
工具準(zhǔn)備
- linQpad
- AdventureWork數(shù)據(jù)庫
-
LinQ查詢操作
LinQ分為查詢表達式和方法語法
-
查詢表達式轉(zhuǎn)換為方法語法再轉(zhuǎn)化為SQL
var data = Enumerable.Range(100, 20);
//LinQ查詢(查詢表達式)
var list1 = from s in data
where s > 115
select s;
//LinQ查詢(方法語法)
var list = data.Where(a => a > 115);
Console.WriteLine(String.Join(",", list1));
Console.ReadKey();
-
投影操作符
- Select 部分列(使用了匿名類型)
- SelectMany 在“父子表”中,使用SelectMany進行查詢
-
過濾操作符
- Where 過濾
- Distinct 會刪除序列中重復(fù)的元素
-
排序操作符
- OrderBy 升序
- OrderByDescending 降序
- ThenBy 多列排序,放在order后繼續(xù)排序
- ThenByDescending 降序
-
分組操作符
- group by into
from p in Persons
group p by p.Name into g
select new {name=p.key,count=g.count}
var a=Persons.GroupBy(p=>p.Name)
.Select(g=>new{
name=g.Key,
Count=g.Coutn()
})
-
連接操作符
- 內(nèi)連接,左外連接和右外連接
- 在進行內(nèi)連接時,必須要指明基于哪個列
var customerList = new List<Customers>
{
new Customers{ customerId=1,Name="張三",PhoneNumber=123,Address="中國" },
new Customers{ customerId=2,Name="李四",PhoneNumber=123,Address="中國" },
new Customers{ customerId=3,Name="王五",PhoneNumber=123,Address="中國" }
};
var infoList = new List<CustomerInfo>
{
new CustomerInfo{ customerId=1, id=1,Number=70681 },
new CustomerInfo{ customerId=4, id=1,Number=70681 },
new CustomerInfo{ customerId=2, id=1,Number=70691 }
};
//連接基于兩個表,以及基于至少一列
//左聯(lián)還是右聯(lián),基于哪個先寫
var cus = from o in infoList
join c in customerList
//基于哪個列
on o.customerId equals c.customerId
select new { c.Name, o.Number };
var cus1 = infoList.Join(customerList,
o => o.customerId,
c => c.customerId,
(o, c) => new {
c.Name, c.Address, o.Number
});
//左外連接,左邊的表中所有的數(shù)據(jù)都會出現(xiàn)在結(jié)果集中,右表要給默認(rèn)值
var leftcus = from c in customerList
join o in infoList
on c.customerId equals o.customerId into p
//右表默認(rèn)值
from item in p.DefaultIfEmpty(new CustomerInfo { id = -1, Number=-1 })
select new { c.Name, c.Address, item.Number };
Console.WriteLine(String.Join("\n", leftcus));
Console.ReadKey();
-
分區(qū)操作符
- Take:從序列的開頭返回指定數(shù)量的連續(xù)元素,相當(dāng)于SQL的TOP N
- Skip:跳過序列中指定數(shù)量的元素,然后返回剩余的元素
-
集合操作符
- 當(dāng)想操作兩個集合時非常有用。
- Union:并集,返回兩個序列的并集,去掉重復(fù)元素,相當(dāng)于SQL的Union。
- Concat:并集,返回兩個序列的并集,不處理重復(fù)元素,相當(dāng)于SQL的Union All。
- Intersect:交集,返回兩個序列中都有的元素,即交集。
- Except:差集,返回只出現(xiàn)在一個序列中的元素,即差集。
-
元素操作符
- First:返回序列中的第一個元素;如果是空序列,引發(fā)異常。
- FirstOrDefault:返回序列中的第一個元素;如果是空,則返回默認(rèn)值default(TSource)。
- Last:返回序列的最后一個元素;如果是空序列,此方法將引發(fā)異常。
- LastOrDefault:返回序列中的最后一個元素;如果是空返回默認(rèn)值default(TSource)。
- Single:返回序列的唯一元素;如果是空或多個元素,引發(fā)異常。該方法非常適合判斷序列是否只包含一個元素(并返回它)。
- SingleOrDefault:返回序列中的唯一元素;如果是空,則返回默認(rèn)值default(TSource);如果該序列包含多個元素,此方法將引發(fā)異常。
-
延遲執(zhí)行
- 返回另外一個序列(通常為IEnumerable<T>或IQueryable<T>)的操作,會延遲執(zhí)行(在最終結(jié)果的第一個元素被訪問的時候才真正開始運算)
- 返回單一值的運算,會立即執(zhí)行
- 強制立即執(zhí)行,可以使用ToList()
反射和動態(tài)語言運行時
- dynamic關(guān)鍵字
- C#4引入動態(tài)類型dynamic,弱類型語言
- dynamic與var的區(qū)域
- 運行時推斷
- var不能作為方法的輸入和返回值類型,dynamic可以
- var不能修飾類或結(jié)構(gòu)的成員,dynamic可以
- 反射
- 在.net中,查看和操作元數(shù)據(jù)的動作稱為反射(Reflection)
- 通過反射加載外部程序集/類型,并調(diào)用方法
- 反射允許你訪問類型任何的成員,包括私有的
//反射
var type = typeof(ReflectionTest);
//獲得類型所有的成員
var members = type.GetMembers();
//通過反射獲得構(gòu)造函數(shù)
var constructor = type.GetConstructors()[0];
//調(diào)用構(gòu)造函數(shù)
object obj = constructor.Invoke(null);
//通過反射調(diào)用實例方法,傳入實例
var method = type.GetMethod("AddTwo");
var o = method.Invoke(obj, new object[] { 100 });
//動態(tài)語言運行時提供的晚期綁定方法
//創(chuàng)建一個ReflectionTest類型的實例
//讓它是一個動態(tài)類型
dynamic dynamicObj = Activator.CreateInstance(typeof(ReflectionTest));
//可以使用普通的方式調(diào)用方法
//編譯器不檢查,不要拼錯
var a = dynamicObj.AddTwo(100);
- 動態(tài)類型簡化晚期綁定
- 普通的反射
- 動態(tài)類型使得方法呼叫更簡單
- 動態(tài)類型簡化COM互操作
- 我們可以使用C#創(chuàng)建并修改Word,Excel文件。這些都是基于COM
- 沒有動態(tài)類型時,訪問COM對象必須使用類型強制轉(zhuǎn)換
- 通過動態(tài)類型簡化代碼
C#5
常量const、字段field、屬性property、構(gòu)造函數(shù)、析構(gòu)函數(shù)、自定義方法、嵌套類
當(dāng)使用new關(guān)鍵字時,自動調(diào)用構(gòu)造函數(shù),構(gòu)造函數(shù)名稱與類相同,public,無返回值。構(gòu)造函數(shù)可以是private(單例模式)
public class car{
public colors color{
get{
return this;
}
//set打開后,get也要打開
};
set{
this=value;//默認(rèn)value
};
}
}
靜態(tài)方法只能使用靜態(tài)字段和屬性。
靜態(tài)成員無需實例化即可使用。
靜態(tài)成員屬于類而非類的實例,但被所有類共享。
實例的方法可以使用靜態(tài)和實例字段和屬性。
當(dāng)你的類中有靜態(tài)函數(shù),C#默認(rèn)會創(chuàng)建一個靜態(tài)構(gòu)造函數(shù),為你的靜態(tài)成員賦值,不能有修飾詞,不能有輸入和輸出。靜態(tài)構(gòu)造函數(shù)只調(diào)用一次。
引用用類型內(nèi)存的初始化需要牽扯到堆上內(nèi)存的分配,因此如果一個自定義的類型==只會包括值類型作為成員==,使用結(jié)構(gòu)是一個較好的選擇
- 方法隱藏,子類和父類方法名稱相同,自動隱藏,子類可以用new關(guān)鍵字
- 方法重寫,子類和父類擁有相同的簽名方法,但子類希望得到不同的行為,而且父類的方法是虛方法或抽象方法。
- 方法重載,子類和父類有相同的方法名稱,但簽名不同。
- sealed修飾類型,不能補繼承
- sealed修飾方法,不能被重寫
- 結(jié)構(gòu)是密封類型,不能被繼承
- 無法實例化一個abstract,子類必須用override來重寫它
- 不能與sealed修飾
- 比較虛的概念使用abstract
- abstract可以有普通的方法,也可以有抽象方法
- 接口-特殊的抽象類,只能有抽象方法
- 抽象方法本身沒有方法體,待子類去實現(xiàn)
- 抽象方法必須在抽象類中
- virtual可以有方法實體,也可以沒有
- 子類可以重寫,也可以不重寫
- 用于父類可以實現(xiàn),但子類有不同實現(xiàn)方式的情景
public class a{
int x;
int y;
public a(){}
public a(int x,int y){
this.x=x;
this.y=y;
}
}
public class b:a{
public b(int x,int y):base(x,y){}
}
- 一個類型有多種狀態(tài)。
- 接口是一種特殊的類,方法只能有簽名,而不能有方法體。
- 使用借口實現(xiàn)has-a關(guān)系。
- 使用接口實現(xiàn)多重繼承,解決單一繼承的缺陷。
- 接口就像技能,通過多重繼承賦予不同的技能。
- ==依賴注入==
- C# 2.0 泛型
- C# 3.0 linq
- C# 4.0 動態(tài)語言
- 數(shù)據(jù)結(jié)構(gòu)是以某種形式將數(shù)據(jù)組織在一起的集合,它不僅存儲數(shù)據(jù),還支持訪問和處理數(shù)據(jù)的操作。
- 簡單分類:線性表,鏈表,哈希表,樹,圖等
- 大部分編程語言都可以實現(xiàn)這些數(shù)據(jù)結(jié)構(gòu),C#已經(jīng)為我們實現(xiàn)了一部分,例如線性表(Array,List,ArrayList,Stack,Queue),鏈表(LinkedList),哈希表(HashTable,Dictionary)等等
- C#中的集合對應(yīng)了一些數(shù)據(jù)結(jié)構(gòu)
- 包括泛型集合與非泛型集合,它們都是不定長的
- 非泛型集合:集合里的元素可以為任意的類型
- 泛型集合:集合里的元素只可以為一種類型T
- 從定義上,數(shù)組(Array)不是集合因為它是定長的
- ArrayList
- Stack 棧
- 后進先出(LIFO)
- 棧是限制插入和刪除只能在一個位置上進行的線性表,該位置是表的末端,叫作棧頂,對棧的基本操作有push(進棧)和pop(出棧),前者相當(dāng)于插入,后者相當(dāng)于刪除最后一個元素
- 練習(xí):棧中全是int,找出棧中最小的元素
- Queue 隊列
- 先進先出(FIFO)
- 隊列是一種特殊的線性表,特殊之處在于它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。
- 裝箱boxing與拆箱unboxing
-非泛型集合的兩個問題:造成裝箱和拆箱\類型不安全- 裝箱的定義:將值類型分配給object變量
- 拆箱的定義:將object變量中的值賦給一個值類型
- 拆箱有風(fēng)險,操作需謹(jǐn)慎
- 泛型集合List<T>
- T稱為類型參數(shù)或占位符
- 實際使用時必須為T指定一個具體類型,例如int,string等,稱為實際參數(shù)
- 棧和隊列的泛型版本
- 由于性能和類型安全,出現(xiàn)泛型后以經(jīng)沒有人使用非泛型合集
- 泛型方法:輸入輸出參數(shù)包括了類型參數(shù)
- 例如:交換任意兩個同類型變量的值
- 使用泛型方法可以達到代碼復(fù)用的目的
- 可以自定義泛型結(jié)構(gòu)和類
- 例如,創(chuàng)建一個自定義的泛型結(jié)構(gòu)
- 泛型約束使得類型參數(shù)T只能是滿足某種條件的類型
- 接口約束:使得泛型的實際參數(shù)必須實現(xiàn)某個接口
- 基類型約束:實參必須是某個類的派生類
- 構(gòu)造函數(shù)約束:泛型實參必須具有可訪問的無參構(gòu)造函數(shù)(默認(rèn)的也可)
public a<T,K>(T b,K c) where T: new() where K:struct
- 定義為Nullable<T> 或者用?
- 一個結(jié)構(gòu),使用HasValue()判斷是否有值
- 定義 有限步操作的集合
- 有線性、確切性、輸入、輸出、可行性
- 時間復(fù)雜度O(S)和空間復(fù)雜度n(S)
- 線性列表
- array 定長 類型安全
- arrayList 非泛型 數(shù)據(jù)類型不安全
- List<T> 泛型
- 鏈表
- 定義:包括節(jié)點和下一個借點的引用。在內(nèi)存中不連續(xù)。
- 雙向鏈表則還包括前一個節(jié)點的引用。
- 好處:節(jié)省內(nèi)存空間,中間插入和刪除的時間復(fù)雜度底。(常數(shù)復(fù)雜度,線性列表為線性)
- 壞處:不能按索引器訪問
-哈希表 hashtable
-哈希表是鍵值對數(shù)據(jù)結(jié)構(gòu)key value
-哈希函數(shù)和哈希碰撞(解決哈希碰撞,線性尋址法,平方尋址法) - 插入和刪除的時間復(fù)雜度都是常數(shù),但哈希表不能排序。
- 節(jié)省空間,速度快
- hashtable非泛型
- Dictionary<K,T>
-中間語言IL,c#通過csc.EXE,先編譯為中間語言,在運行時,CLR通過JIT即使編譯為機器碼。
委托 delegate
- 委托是一個特殊的類,是密封袋,不可以繼承。包括一個特殊的方法invoke,也可以看成一個簽名。
- 使用委托傳入方法,起到代碼復(fù)用的目的
- 可以為委托提供特殊的簽名方法
委托的原理:
- 委托是一個密封類,它繼承自System.MulticastDelegate,后者再繼承自System.Delegate
這個類的成員是固定的,這個密封類包括一個構(gòu)造函數(shù)和三個核心函數(shù),Invoke方法賦予其同步訪問的能力,BeginInvoke,EndInvoke賦予其異步訪問的能力 - 委托鏈:
- System.MulticastDelegate是所有委托的父類,它含有一個非常重要的字段_invocationList,指的是委托自身的方法鏈,它是一個Delegate類型的數(shù)組,所以委托可以掛接多于一個函數(shù)(即一個函數(shù)List),可以為這個鏈自由的添加或刪除函數(shù)。
- 一個委托鏈可以沒有函數(shù)如果執(zhí)行委托,將會順序的執(zhí)行委托鏈上所有的函數(shù)。如果某個函數(shù)出現(xiàn)了異常,則其后所有的函數(shù)都不會執(zhí)行如果你的委托的委托鏈含有很多委托的話,你只會收到最后一個含有返回值的委托的返回值
- 委托鏈?zhǔn)褂?=將方法鏈接。
- System.Delegate是System.MulticastDelegate的父類,它含有2個非常重要的字段_target和_methodPtr。如果委托指向了一個實例方法,_target將會等于該實例本身。在方法中,可以通過this訪問。如果委托指向了一個靜態(tài)方法,_target將會等于null無論委托指向什么方法,_methodPtr都是CLR用于標(biāo)示委托指向方法的IntPtr值
- 委托的構(gòu)造函數(shù)設(shè)置_invocationList,_target和_methodPtr在進行調(diào)用時,Invoke方法使用_target和_methodPtr在指定對象上調(diào)用對應(yīng)的方法
事件
- 事件必須包括三部分:
- 事件的訂閱者(Subscriber),訂閱者會在事件觸發(fā)之后,做出響應(yīng)行為。
- 事件的觸發(fā)者,或者發(fā)布者(Publisher),條件得到滿足時,觸發(fā)事件訂閱者和觸發(fā)者之間的數(shù)據(jù)傳送通道。
- 事件使用步驟
- 聲明一個事件處理方法,指出事件發(fā)生時,訂閱者應(yīng)該有怎樣的反應(yīng)。該方法可以傳入繼承自EventArgs類型的自定義數(shù)據(jù)
- 聲明委托。委托的簽名和事件處理方法相同
- 聲明基于該委托的事件
- 為事件增加訂閱者
- 在事件符合條件時,調(diào)用事件
- 事件本身是私有的
- 事件會被轉(zhuǎn)化為一個私有的字段,以及兩個方法。字段的類型和事件關(guān)聯(lián)的委托相同,而兩個方法的輸入類型和事件關(guān)聯(lián)的委托相同,沒有輸出
- 事件將字段改為私有,從而令外部不能更改它的值
- 在事件所處的對象之外,事件只能出現(xiàn)在+=,-=的左方
泛型委托
- C# 2 中提供了兩個泛型委托Action和Func
- Action是沒有輸出的,F(xiàn)unc可以有一個輸出
- 使用它們可以代替原始的delegate關(guān)鍵字
委托實際上就是函數(shù)指針,將不同函數(shù)像變量一樣傳遞,供不同參數(shù)調(diào)用。
- 使用var關(guān)鍵字
- 只能對局部變量使用隱式類型
- 在foreach中一般不需要指定類型
- 匿名類型允許你直接在花括號內(nèi)建立類型,而無需指定類名稱
- 只能用var或object來修飾,用var修飾可以用點來獲取成員
- 如果兩個匿名類型有相同的成員,且成員類型相同,順序相同,編譯器會將它們做為同一個類型。
- 在不創(chuàng)建子類,不更改類型本身的情況下,修改類型
- 擴展方法必須定義于靜態(tài)的類型中,且擴展方法必須是靜態(tài)的。
- 擴展方法第一個輸入?yún)?shù)要加上this,擴展方法必須至少有一個輸入?yún)?shù)
//匿名類型
var s = new { name = "小李", age = 15 };
var b=s.name;
Console.WriteLine(s.GetType());//返回<>f__AnonymousType0`2[System.String,System.Int32]
//擴展方法
public static class StringExtension //1、在靜態(tài)類中,2、一般用Extension做為后綴
{
public static bool isHuiwen(this string input)
{
//算法
return true;
}
}
bool a = "string".isHuiwen//調(diào)用
- C#3為了令代碼更加簡化,引入了lambda表達式
- lambda表達式,是表達式的一種,它可以表式一個函數(shù)
- 基本結(jié)構(gòu)分三個部分
- 輸入?yún)?shù) 一個參數(shù)不需要括號,多個參數(shù)或沒有參數(shù)需要括號
- 箭頭 =>
- 表達式或語名塊 如有只一條件語句不需要花括號,多行需要花括號
//old:泛型委托實現(xiàn)字符串判定
static void Main(string[] args)
{
var data = new List<string> { "string", "this", "the", "a", "dog" };
var func = new Func<string, bool>(Predicate);//泛型委托實現(xiàn),string輸入的參數(shù),bool返回的類型
//使用匿名方法的方式,不用在單獨定義Predicate這個方法
var func1 = new Func<string, bool>(delegate (string input)
{
return input.Length >= 4;
});
//lamdbe表達式寫法
Func<string, bool> func2 =(input)=>{
return input.Length >= 4;
};
foreach (var word in data)
{
if (func(word))
{
Console.WriteLine(word);
}
}
Console.ReadKey();
}
public static bool Predicate(string input)//使用匿名方法就這用寫這個了
{
return input.Length >= 4;
}
工具準(zhǔn)備
- linQpad
- AdventureWork數(shù)據(jù)庫
LinQ查詢操作
LinQ分為查詢表達式和方法語法
-
查詢表達式轉(zhuǎn)換為方法語法再轉(zhuǎn)化為SQL
var data = Enumerable.Range(100, 20); //LinQ查詢(查詢表達式) var list1 = from s in data where s > 115 select s; //LinQ查詢(方法語法) var list = data.Where(a => a > 115); Console.WriteLine(String.Join(",", list1)); Console.ReadKey(); -
投影操作符
- Select 部分列(使用了匿名類型)
- SelectMany 在“父子表”中,使用SelectMany進行查詢
-
過濾操作符
- Where 過濾
- Distinct 會刪除序列中重復(fù)的元素
-
排序操作符
- OrderBy 升序
- OrderByDescending 降序
- ThenBy 多列排序,放在order后繼續(xù)排序
- ThenByDescending 降序
-
分組操作符
- group by into
from p in Persons group p by p.Name into g select new {name=p.key,count=g.count} var a=Persons.GroupBy(p=>p.Name) .Select(g=>new{ name=g.Key, Count=g.Coutn() }) -
連接操作符
- 內(nèi)連接,左外連接和右外連接
- 在進行內(nèi)連接時,必須要指明基于哪個列
var customerList = new List<Customers> { new Customers{ customerId=1,Name="張三",PhoneNumber=123,Address="中國" }, new Customers{ customerId=2,Name="李四",PhoneNumber=123,Address="中國" }, new Customers{ customerId=3,Name="王五",PhoneNumber=123,Address="中國" } }; var infoList = new List<CustomerInfo> { new CustomerInfo{ customerId=1, id=1,Number=70681 }, new CustomerInfo{ customerId=4, id=1,Number=70681 }, new CustomerInfo{ customerId=2, id=1,Number=70691 } }; //連接基于兩個表,以及基于至少一列 //左聯(lián)還是右聯(lián),基于哪個先寫 var cus = from o in infoList join c in customerList //基于哪個列 on o.customerId equals c.customerId select new { c.Name, o.Number }; var cus1 = infoList.Join(customerList, o => o.customerId, c => c.customerId, (o, c) => new { c.Name, c.Address, o.Number }); //左外連接,左邊的表中所有的數(shù)據(jù)都會出現(xiàn)在結(jié)果集中,右表要給默認(rèn)值 var leftcus = from c in customerList join o in infoList on c.customerId equals o.customerId into p //右表默認(rèn)值 from item in p.DefaultIfEmpty(new CustomerInfo { id = -1, Number=-1 }) select new { c.Name, c.Address, item.Number }; Console.WriteLine(String.Join("\n", leftcus)); Console.ReadKey(); -
分區(qū)操作符
- Take:從序列的開頭返回指定數(shù)量的連續(xù)元素,相當(dāng)于SQL的TOP N
- Skip:跳過序列中指定數(shù)量的元素,然后返回剩余的元素
-
集合操作符
- 當(dāng)想操作兩個集合時非常有用。
- Union:并集,返回兩個序列的并集,去掉重復(fù)元素,相當(dāng)于SQL的Union。
- Concat:并集,返回兩個序列的并集,不處理重復(fù)元素,相當(dāng)于SQL的Union All。
- Intersect:交集,返回兩個序列中都有的元素,即交集。
- Except:差集,返回只出現(xiàn)在一個序列中的元素,即差集。
-
元素操作符
- First:返回序列中的第一個元素;如果是空序列,引發(fā)異常。
- FirstOrDefault:返回序列中的第一個元素;如果是空,則返回默認(rèn)值default(TSource)。
- Last:返回序列的最后一個元素;如果是空序列,此方法將引發(fā)異常。
- LastOrDefault:返回序列中的最后一個元素;如果是空返回默認(rèn)值default(TSource)。
- Single:返回序列的唯一元素;如果是空或多個元素,引發(fā)異常。該方法非常適合判斷序列是否只包含一個元素(并返回它)。
- SingleOrDefault:返回序列中的唯一元素;如果是空,則返回默認(rèn)值default(TSource);如果該序列包含多個元素,此方法將引發(fā)異常。
延遲執(zhí)行
- 返回另外一個序列(通常為IEnumerable<T>或IQueryable<T>)的操作,會延遲執(zhí)行(在最終結(jié)果的第一個元素被訪問的時候才真正開始運算)
- 返回單一值的運算,會立即執(zhí)行
- 強制立即執(zhí)行,可以使用ToList()
- C#4引入動態(tài)類型dynamic,弱類型語言
- dynamic與var的區(qū)域
- 運行時推斷
- var不能作為方法的輸入和返回值類型,dynamic可以
- var不能修飾類或結(jié)構(gòu)的成員,dynamic可以
- 在.net中,查看和操作元數(shù)據(jù)的動作稱為反射(Reflection)
- 通過反射加載外部程序集/類型,并調(diào)用方法
- 反射允許你訪問類型任何的成員,包括私有的
//反射
var type = typeof(ReflectionTest);
//獲得類型所有的成員
var members = type.GetMembers();
//通過反射獲得構(gòu)造函數(shù)
var constructor = type.GetConstructors()[0];
//調(diào)用構(gòu)造函數(shù)
object obj = constructor.Invoke(null);
//通過反射調(diào)用實例方法,傳入實例
var method = type.GetMethod("AddTwo");
var o = method.Invoke(obj, new object[] { 100 });
//動態(tài)語言運行時提供的晚期綁定方法
//創(chuàng)建一個ReflectionTest類型的實例
//讓它是一個動態(tài)類型
dynamic dynamicObj = Activator.CreateInstance(typeof(ReflectionTest));
//可以使用普通的方式調(diào)用方法
//編譯器不檢查,不要拼錯
var a = dynamicObj.AddTwo(100);
- 普通的反射
- 動態(tài)類型使得方法呼叫更簡單
- 我們可以使用C#創(chuàng)建并修改Word,Excel文件。這些都是基于COM
- 沒有動態(tài)類型時,訪問COM對象必須使用類型強制轉(zhuǎn)換
- 通過動態(tài)類型簡化代碼
C#2泛型
C#3LinQ
C#4動態(tài)語言運行時
C#5多線程
C#6-7
C#6-2015年7月,Vs2015 .net framework4.6語法糖
C#7-2017年3月,Vs2017 .net framework4.6.2增加元組和模式匹配,更具有函數(shù)式語言特性
- 字符串插值
string name="張三";
string s="123";
//old
string.Format("{0}-{1}",name,s);
//new
$"{name}-{s}";
- 使用static using呼叫靜態(tài)類方法
- 為了降低代碼的冗余,我們希望在呼叫靜態(tài)類的方法時,可以省去類的名稱
- 判斷null的簡寫操作符?.
- 利用新的操作符“?.”(稱為nullet)可以簡化代碼
- 異常過濾
- 現(xiàn)在可以在catch后面跟when子句進一步對異常進行過濾并操作
- 對于一些來自第三方傳回的帶有固定異常代碼的異常非常有用
- [C# 7]改進的out關(guān)鍵字
- 現(xiàn)在,out關(guān)鍵字是即插即用的
- 允許以_(下劃線)形式“舍棄”某個out 參數(shù)
- [C# 7]值類型元組(ValueTuple)
- 元組(Tuple)是一個任意類型變量的集合,并最多支持八個變量
- C# 4中元組最大的兩個問題是:
- Tuple類將其屬性命名為 Item1、Item2等,這些名稱是無法改變的,因此只會讓代碼可讀性變差
- Tuple類是引用類型,使用任一Tuple類型即意味著在堆上分配對象,因此,會對性能有負(fù)面影響,還不如辛苦一點手寫一個結(jié)構(gòu)體
- C# 7引入的新元組(ValueTuple),解決了上面兩個問題,它是一個結(jié)構(gòu)體,并且,你可以傳入描述性名稱(TupleElementNames屬性)以便之后更容易的調(diào)用它們
- 作為方法的參數(shù)和返回值,返回多個變量