隱式類型
C#3.0。.NET Fraemork3.5出來的。
編譯器自動推算出來類型,語法糖。

#region 隱式類型 var a = 1; var b = "1"; var c = 1.23m; var d = 1.23; Console.WriteLine(a.GetType()); Console.WriteLine(b.GetType()); Console.WriteLine(c.GetType()); Console.WriteLine(d.GetType()); #endregion
關鍵字var,編譯的時候會根據右邊的類型判斷左邊變量的具體類型。
注意:是編譯為中間語言的時候確定的。不影響性能。
匿名類型

#region 匿名類型 string Name = "藏鋒"; //字段名字,Name根據上面定義的Name起名的。ID是自己定義的名字。 var user = new { Name, ID = 1 }; Console.WriteLine(user.Name); Console.WriteLine(user.ID); #endregion
不用實現寫一個類,也可以創建一個對象,並且訪問他的成員。
注意:成員名字可以自己定義。 也可以把其他成員放進來(默認名字和其他成員一樣,也可以自己在指定)
自動屬性

#region 自動屬性 屬性不能定義在方法里面 int _UserId; public int UserID { get { return _UserId; } set { _UserId = value; } } //自動屬性,生成的中間語言和 先定義一個私有變量,在寫他的訪問器一樣。 public string UserNmae { get; set; } #endregion
自動屬性在編譯的時候生成他對應的私有字段。和get ,set方法。
初始化器

#region 初始化 var stuInit = new Student() { Name = "藏鋒", ID = 1 }; IList<int> listInt = new List<int>() { 1, 2, 3, 4 }; //無論是值類型還是引用類型使用的時候都要初始化 賦值。 //值類型一般有默認值0 //引用類型為NULL Student stuNULL; int intNULL; string strNULL; string refStr = string.Empty; int refID =0; Console.WriteLine(refStr); Console.WriteLine(refID); //傳出參數使用的時候不需要賦值,原因在方法里面會給他賦值。 string outStr; int outID; stuInit.GetUserName("WYX", ref refStr, ref refID, out outStr, out outID); #endregion
就是在對象創建的時候初始化他的一些值。
委托

#region 委托 Student stuForDele = new Student(); StuDele stuDele = new StuDele(stuForDele.GetUserId); stuDele += stuForDele.GetUserId1; stuDele += stuForDele.GetUserId2; stuDele += stuForDele.GetUserId3; stuDele -= stuForDele.GetUserId3; Console.WriteLine(stuDele(2)); #endregion
委托是一個類型,和CLASS是一個級別的。
泛型
泛型類,泛型方法,泛型委托

#region 泛型 DAO<Teacher> dao = new DAO<Teacher>(); Teacher teac = new Teacher(); var pros1 = dao.GetAllPro(); var pros2 = dao.GetAllPro(teac); Student stRef = new Student(); var typeName = dao.GetTypeName<Student>(stRef); foreach (var item in pros1) { Console.WriteLine(item); } foreach (var item in pros2) { Console.WriteLine(item); } Console.WriteLine(typeName); #endregion
面試經常問的,泛型特點:1,類型安全。2,節約裝箱拆箱操作,性能比較好。3,代碼重用擴展性強。4,可讀性強
1類型安全:用的是定義好的類型,不用object
2性能高:比如說原來一個方法要根據參數做不同的事,參數類型還不一樣。那么就只能把參數設置為object,這樣就要裝箱拆箱操作了。
3代碼重用:新建的類型只要符合泛型約束就可以用泛型的方法。
原理:泛型每次編譯的時候都會都會生成一套對應的類方法。和在程序里面一個一個寫方法一樣的。
默認值:通過default關鍵字,將null賦予引用類型,將0賦予值類型。不能直接給泛型賦NULL或者0,因為不知道他是什么類型。
泛型委托
.NET框架自帶三個系統泛型委托
1、public delegate bool Predicate<in T>(T obj);

// // 摘要: // 表示定義一組條件並確定指定對象是否符合這些條件的方法。 // // 參數: // obj: // 要按照由此委托表示的方法中定義的條件進行比較的對象。 // // 類型參數: // T: // 要比較的對象的類型。此類型參數是逆變。即可以使用指定的類型或派生程度更低的類型。有關協變和逆變的更多信息,請參見泛型中的協變和逆變。 // // 返回結果: // 如果 obj 符合由此委托表示的方法中定義的條件,則為 true;否則為 false。
一個傳入參數,返回類型為bool
2、public delegate void Action<in T>(T obj);

// // 摘要: // 封裝一個方法,該方法只有一個參數並且不返回值。 // // 參數: // obj: // 此委托封裝的方法的參數。 // // 類型參數: // T: // 此委托封裝的方法的參數類型。此類型參數是逆變。即可以使用指定的類型或派生程度更低的類型。有關協變和逆變的更多信息,請參見泛型中的協變和逆變。
沒有返回值,0-16個傳入參數
3、public delegate TResult Func<out TResult>();

// // 摘要: // 封裝一個不具有參數但卻返回 TResult 參數指定的類型值的方法。 // // 類型參數: // TResult: // 此委托封裝的方法的返回值類型。此類型參數是協變。即可以使用指定的類型或派生程度更高的類型。有關協變和逆變的更多信息,請參見泛型中的協變和逆變。 // // 返回結果: // 此委托封裝的方法的返回值。
最后一個參數為傳出參數,一定有傳出參數。前面0-16個傳入參數。

#region 泛型委托 Student stupre = new Student() { ID = 10, Name = "WYX" }; Predicate<int> pStu = new Predicate<int>(stupre.CheUsreID); Console.WriteLine(pStu(stupre.ID)); //0-16個參數 17中方法重載 Action ac1 = new Action(stupre.GetUserNameNoP); ac1(); Action<string> ac2 = new Action<string>(stupre.GetUserNameNoP); ac2("123"); //最后一個參數為返回參數out 協變 ,前面可以放0-16個參數 Func<string> fun1 = new Func<string>(stupre.ToString); fun1(); Func<int, int> fun2 = new Func<int, int>(stupre.GetUserId3); Console.WriteLine(fun2(5)); #endregion

//自定義泛型委托 NoInOutDele<int> custom1 = new NoInOutDele<int>(stupre.GetUserNameP); Console.WriteLine(custom1()); OnePDele<string> custom2 = new OnePDele<string>(stupre.GetUserNameP); Console.WriteLine(custom2("藏鋒")); OutPDele<string, string, string> custom3 = new OutPDele<string, string, string>(stupre.GetUserNameNoP); Console.WriteLine(custom3("123", "456"));
匿名方法
delegate(){}

#region 匿名方法 //委托里面添加的是一個個方法,可以寫一個匿名方法給他 delegate(){} //好處 可以訪問上下文變量 OutPDele<string, string, string> unNameFun1 = new OutPDele<string, string, string>(delegate (string s1, string s2) { return s1 + s2; }); Console.WriteLine(unNameFun1("藏", "鋒")); #endregion
語法糖,編譯器會幫我們聲明一個方法。(CLS中規定的是只有方法和字段)
好處:可以訪問上下文變量。
什么代碼可讀性好,我感覺根據個人情況吧。編譯到中間語言都一個樣。
注意:匿名方法不能復制給變量 var g = delegate (string s) { Console.WriteLine(s); }
Lambda表達式
匿名方法的更加簡單寫法。把匿名方法的delegate換成=>。編譯器也會把他生成一個方法。
無論是匿名方法還是Lambda表達式都是依賴於委托存在的。只有委托才調用他們。
定義:表達者委托或表達式樹的匿名方法。 本質是一個匿名方法,有時候這種代碼看不懂得時候就想想他的本質,委托調用他們。

#region Lambda OutPDele<string, string, string> lamdFun = new OutPDele<string, string, string>((string s1, string s2) => { return s1 + s2; }); //=>讀 gos to //一個參數可以(string s)=>s或者s=>s //多個參數(string s1,string s2.....) 一定加括號 //返回只有一條語句返回 s=>{return s;} 或者s=>s (默認為返回) //多條語句 s=>{吧啦吧啦} OnePDele<string> lamdFun1 = new OnePDele<string>(s => { return s; }); #endregion

//表達是一個委托 OutPDele<string, string, string> lamdFunD = (string s1, string s2) => s1 + s2; lamdFunD.Invoke("1", "2");
Lambda表達式是一個匿名方法。語法:形參列表=>(goes to)方法體。“=>”運算符具有與“=”相同的優先級,並且是右結合性運算符。
一個參數可以(string s)=>s或者s=>s
多個參數(string s1,string s2.....) 一定加括號
返回只有一條語句返回 s=>{return s;} 或者s=>s (默認為返回)。去掉了大括號
只有一條邏輯語句的例如 s=>{Console.WriteLine("1");} 或者s=>Console.WriteLine("1"); 去掉了大括號
多條語句 s=>{吧啦吧啦}
Lambda表達式規則有三個
1、 Lambda包含的參數數量必須與委托類型包含的參數數量相同。
2、每個輸入參數必須都能夠隱式轉換為其對應的委托參數。(逆變)
3、返回值(如果有)必須能夠夠轉換為委托的返回類型。(協變)
擴展方法
.net framork3.0
目的:給一個類型增加行為
結構:靜態類(不能嵌套,泛型),靜態方法,方法第一個參數為 (this 擴展的類型 調用這個擴展方法的對象 ),后面參數為擴展方法的參數。(this string var, string s1) (this string var, string s1, string s2)
使用:在使用的類中添加他的命名空間,優先調用實例方法。

#region 擴展方法 string s = "藏鋒"; Console.WriteLine(s.MargTwoStr("1", "2")); Console.WriteLine(s.MarMySelf("23")); #endregion
注意:
Console.WriteLine(s.MarMySelf("23"));
//上面和下面編譯玩之后都一樣的。只是上面的寫的更友好。
Console.WriteLine(StrExc.MarMySelf("1", "2"));
THIS:四種用法
1,當前類的實例

public string GetUserNameNoP(string p_Name, string p_Name1) { return p_Name1 + this.Name + p_Name; }
2,串聯構造函數
調用一個構造函數的時候,他再去調用另外 一個構造函數this(若干構造函數參數)
如果要構造基類的,就是base(若干構造函數參數)

public Teacher() { } public Teacher(string p_TeacherName):this() { } public Teacher(string p_TeacherName, DateTime p_CreateDate) : base(p_CreateDate) { }
3,當前模塊提到的擴展方法。
4,索引器

public class MyIndex { private string[] strList = new string[10]; public string this[int index] { get { return strList[index]; } set { strList[index] = value; } } } public class MyIndexStr { //用string索引查看的葯用hashtable key value列表 private Hashtable hash = new Hashtable(); public string this[string indexstr] { set { hash.Add(indexstr, value); } get { return hash[indexstr].ToString(); } } }
講一個面試經歷:有一個公司在咖啡館面試的(其實是一個小公司,沒有辦公地點),所有開發語言都招,去了先根據業務設計一個算法,在填寫的英文簡歷,通過后才面談,剛開始講一大推諷刺的話,在講來我們這里可以學到好多東西,(深深的套路呀)。然后開始問專業問題,中括號怎么用的,我說 索引器,他愣了一下(后來我猜他問的是JQ里面[]屬性選擇器,他做前端的,可能不懂后台),后面就開始嘲諷我,然后我懟了他兩句走了。見到這種人不要慫,他們也是能忽悠到一個算一個。
還有一次,他們公司開發三個人,每天加班到八點,周六也加班,然后說實行末位淘汰制,我想三個人你還淘汰啥。
迭代器
foreach的時候就是進行迭代操作,但是foreach遍歷的集合必須繼承IEnumerable接口實現了GetEnumerator方法(IEnumerable只有GetEnumerator()一個方法)。
foreach數組的時候,其實編譯器把foreach變成for了。
迭代器的優點:對於一個較大的集合,不用一次性把數據全部加在出來。不需要遍歷的時候一次加在一個。
yield:在迭代器塊中用於向枚舉數對象提供值或發出迭代結束信號。(迭代器中用的就是這個東西,一次返回一個值)

#region 迭代器 都要繼承IEnumerable接口實現了GetEnumerator方法。 List<Teacher> teas = new List<Teacher>(); for (int i = 0; i < 10; i++) { Teacher tae = new Teacher() { CreateUserID = i, TeacherName = i.ToString(), CreateDate = DateTime.Now }; teas.Add(tae); } //我們foreach的時候就是使用的迭代器 //但是數組也可以使用foreach,那是因為編譯器把他轉化為了for。中間語言中還是for //IEnumerable接口中只有一個方法。 foreach (var item in teas) { Console.WriteLine(item.TeacherName); } foreach (var item in GetIterator()) { Console.WriteLine(item.ToString()); } //foreach遍歷的集合每次只會返回一個,不把所有的都加載。GetIterator()方法一段一段的執行,遍歷中間結束,后面的代碼也不會執行 foreach (var item in GetIterator()) { if(item==2) { break; } } #endregion

//yield :在迭代器塊中用於向枚舉數對象提供值或發出迭代結束信號。 static IEnumerable<int> GetIterator() { Console.WriteLine("迭代器返回了1"); yield return 1; Console.WriteLine("迭代器返回了2"); //終止迭代 //yield break; yield return 2; Console.WriteLine("迭代器返回了3"); yield return 3; }
注意事項:
1:做foreach循環時多考慮線程安全性,在foreach時不要試圖對被遍歷的集合進行remove和add等操作任何集合,即使被標記為線程安全的,在foreach的時候,增加項和移除項的操作都會導致異常
2:IEnumerable接口是LINQ特性的核心接口,只有實現了IEnumerable接口的集合,才能執行相關的LINQ操作,比如select,where等這些操作。
LINQ
前面都是准備知識,現在進入正題。
分為兩種模式
1,查詢操作符(擴展方法+lambda),擴展方法 擴展的事IEnumerable<T>接口。所以linq的基礎都是集合要繼承IEnumerable<T>。
過濾:where,Find,FindAll,FindLast,First<T>,FirstOrDefault<>
統計函數:Count,MIn,Sum,Max
排序:OrderBy,OrderByDescending
跳過前面多少條數據取余下的數據:SKIP
從開始起獲取指定數量的數據:TAKE
模糊匹配:Contains
分組:GroupBy
連接查詢:Join
投影:select(select t或者select(t=>new{t.1,t.2}))
2,查詢表達式
From [type] id in source
[join [type] id in source on expr equals expr [into subGroup]]
[from [type] id in source|let id=expr | where condition]
[orderby ordering,ordering,ordering,ordering...]
select select expr | group expr by key
[into id query]
tips:type是可選的,id是數據源集合中的一項,source是數據源集合, 其實是在一直循環source,每次循環把值放入select后面
expr表示一個表達式,subGroup是一個臨時變量,繼承自IGroup,代表一個分組。
可以有多個form,多個where,set指定臨時變量
可以有0-多個排序 orderby a descing orderby b。 orderby a descing ,b,c
select new投影(匿名類,返回的類型用var 因為他生成的時候一后台定義的類型集合),group 類型 by 具體屬性。標准linq 前面一般寫一個var(不確定類型)。 一個查詢表達式必須以select或者group by結束。select后跟要檢索的內容。group by 是對檢索的內容進行分組
group by 他后面只能跟 into is as 關鍵字,常用into 關鍵字放入臨時數據源 然后在select 投影。
into放入臨時數據源
Let 設置變量
注意:這種查詢表達式會編譯成上面的擴展方法+lambad。這種是一個語法糖。
推薦文章:http://www.cnblogs.com/liulun/archive/2013/02/26/2909985.html
linqpad工具:http://www.linqpad.net/Download.aspx (EF也可以用)(但是linq to sql,EF中都不建議使用linq,特別是復雜的查詢。linqtosql貌似已經死了)