語法糖
維基百科給出的解釋:計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。從這個解釋中我們可以看出兩點,一是語法糖不會影響程序的功能,再者,語法糖會讓編寫者更開心,閱讀者更舒心。對于許多不愿意使用這些語法還吐糟說看不懂,學習成本高的朋友,我只能呵呵啦,有本事你去寫匯……哦不,機器語言啊,現代的編程語言其實也都是機器語言的語法糖了呢。
C#中的語法糖
懶人定律說,能坐著的就不站著,能躺著的就不坐著,能少敲點代碼就少敲點代碼,能少費點腦力就少費點腦力。微軟不僅給我們提供了全宇宙最強大的IDE,還給我們提供了諸多牛x得不行的語言特性,作為.net陣營的忠實粉,我沒有理由不去詳細了解這些優秀的語法規則,做一個聰明的懶人。
爪哇的開發人員對以下的代碼一定不會陌生:
PRivate int _value;public int GetValue(){ return _value;}public void SetValue(int value){ _value = value;}當然,在.NET的版本中也時而可見這樣的代碼,對于隨處可見的這樣的代碼,我們實在忍無可忍,于是有了這樣的代碼:
private int _value;public int value{ get{return _value;} set{_value=value;}}簡短了很多,我們還可以用VS提供的強大的快捷鍵Ctrl+R,R來快速生成代碼,但是為什么每一個屬性的背后都一定要有一個賢惠的從不出聲的局部變量呢?所以誕生了這樣的代碼
public int Value{get;set;}如果你用反編譯軟件查看,你會在IL代碼里發現,編輯器幫我們生成了get_Value和set_Value這樣的方法。
如果你想讓這個屬性只能在類內部進行賦值操作,完全可以在set前面加了private限定符,protected和public也都是支持的哦。如果你已經用上了C# 6.0,你還可以這樣使用屬性
public string Value{get;set;}="Hello World";當然,屬性的魅力并不只是因為它簡單、安全、靈活,很多時候,我們會寫出如下代碼
private string _mark="";public string Mark{ get { if(!string.IsNullOrWhiteSpace(_mark)) return _mark; if(Score>=80 && Score<90) { _mark= "良"; } else if(Score>=60 && Score<80) { _mark= "中"; } else if(Score>=90) { _mark= "優"; } else if(Score<60) { _mark= "不及格"; } return _mark; }}一定程度上來講,屬性就是 包含了邏輯代碼的變量,還有些時候,我們需要這樣寫:
private string _mobile="";public string Mobile{ get { if(!string.IsNullOrWhiteSpace(_mobile))return _mobile; if(!HasMobile) return ""; //從數據庫里獲取 balabalabala }}按需獲取 ,而不是每次加載都獲取,使用屬性還能提高性能,對于屬性,兩個字:牛x。
對于var關鍵字,需要說的不多,看一下就知道。
//傳統定義int value=100;//使用var定義var value=100;在IL中,以上兩句代碼是完全相同的。因為C#是強類型的編程語言,使用var定義的變量同樣是強類型的,當我們放到變量上的時候,萬能的VS會在我們為我們提示出它所推斷出的CLR類型,如果你對var定義的變量使用點的話,它還會列出該類型的所有成員。
var i = 0;var l = 0L;var s = "Hello Boy";var b = true;var sb = new StringBuilder();var rnd = new Random();var fs = new FileStream("c:/test.txt", FileMode.OpenOrCreate, Fileaccess.ReadWrite, FileShare.ReadWrite);int ii = 0;long ll = 0L;string str = "Hello Boy";bool bb = true;StringBuilder sbsb = new StringBuilder();Random rndnd = new Random();FileStream fss = new FileStream("c:/test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); 對于這兩類寫法,是仁者見仁,智者見智的,不過我更喜歡使用var的方式。
需要注意的是,使用var的時候,必須給變量賦值以推斷出變量的類型,否則編譯失敗
有些朋友又該疑惑了,不是說C#是強類型的編程語言么,怎么又會出現 沒有類型的對象?且看如下代碼:
var person = new { Name="Tom",Age=20};這種類似JSON的對象又是什么呢,IL會告訴我們如下信息
.locals init ( [0] class '<>f__AnonymousType0`2'<string, int32> person)IL_0000: nopIL_0001: ldstr "Tom"IL_0006: ldc.i4.s 20IL_0008: newobj instance void class '<>f__AnonymousType0`2'<string, int32>::.ctor(!0, !1)IL_000d: stloc.0所以,匿名類型并不是沒有類型,而是在編譯時,編譯器會為我們生成一個類型,同時,如果你再深入一點仔細看它生成的類,它并沒有對屬性的set方法,因而 匿名類型是只讀的。
匿名類型在linq中的應用是比較多,有了它,我們不需要定義一堆的類來進行linq的解析,但這種類型在運行時又確確實實是作為一個實在的類存在,這種語法規則是相當優雅行而有效的。
dynamic a = 1;a = "b";編譯并不會報錯!很神奇的代碼吧, 給什么值就是什么類型,MVC里的ViewBag就是這貨了。
dynamic mbag=new ExpandObject();mbag.intValue=10;mbag.message="hello";Action<string> act=(str)=>(console.write(str);)mbag.say=act;mbag.say("hello");var p=new Person();p.Name="Tom";p.Age=20;var p=new Person{ Name="Tom", Age=20 };連小括號都不要哦,強大。
using(var conn = new SqlConnection(connStr)){ var cmd = conn.CreateCommand(); cmd.CommandText = "GetPageRecord"; cmd.Parameters.AddRange(new SqlParameter[]{ new SqlParameter("@pageSize",SqlDbType.Int){Value=20}, new SqlParameter("@pageIndex",SqlDbType.Int){Value=2}, new SqlParameter("@totalCount",SqlDbType.BigInt){Direction=ParameterDirection.Output} }); var records=cmd.Execute(); var count = (int)cmd.Parameters["totalCount"].Value;}我常常會在sql傳參的時候使用這種方式
與類型初始化器類似的,我們可以對List、Dictionary等集合進行這樣的初始化。
var list = new List<string>{ "Tom","Jake","Jerry","Mary"};var dic = new Dictionary<int, string>{ {1001,"Tom"}, {1002,"Jake"}, {1003,"Jerry"}, {1004,"Mary"}};如果你已經用上了C#6.0,可以這樣寫
var dic = new Dictionary<int, string>{ [1001]="Tom", [1002]="Jake", [1003]="Jerry", [1004]="Mary"};using(var conn=new SqlConnection(connStr)){ //balabalabala}//等同于SqlConnection conn;try{ conn=new SqlConnection(connStr); //balabalabala}finally{ if(conn!=null)conn.Dispose();}using后會調用對象的Dispose方法,所以 使用using包起來的類型須實現IDispose接口。
作為唯一的三目運算符?:自不必多說
var str=a>=b?"a大于等于b":"b大于a";對于??
object a = null;var b = a ?? "a為空";//等同于var b=a!=null?a:"a為空";很清楚吧,b的類型與a相同。
如果已經用上了C# 6.0,還可以這樣寫
int? GetCount(){ return list?.Count;}//等同于int? GetCount(){ if(list==null) return null; return list.Count;}對于這兩者,大可寫無數篇幅來進行討論,以下僅列舉出簡單的例子
delegate string SDelegate(string a);string M1(string a){ return a.ToUpper();}void test(){ d = new SDelegate(M1); //等同于 d = new SDelegate(delegate(string a) { return a.ToUpper(); }); //等同于 d = new SDelegate((a) => { return a.ToUpper(); }); //等同于 d = new SDelegate(a=>a.ToUpper()); d("aaa"); //=AAA}在VS中輸入Enumerable,然后F12吧,好多好多,舉例如下:
var list=goods .Where(x=>x.Age>80) .OrderBy(x=>x.Score) .Skip((pageIndex-1)*pageSize) .Take(pageSize) .Select(x=>new { Name=x.Name, Age=x.Age, Score=x.Score }) .ToList();是不是特別優雅。
擴展方法使得使用者可以向現有的類型“添加”方法而無須創建新的類型、重新編譯或以其他方式修改原始類型,從表現上看,擴展方法是靜態方法,但可以像實例方法一樣被調用。其實上述linq的方法也大多是對[System.Collections.IEnumerable]和[System.Collections.Generic.IEnumerable<T>]的擴展。public static class Exten{ public static String HtmlDecode(this string o) { if (o == null) return ""; var s = o.ToString(); if (string.IsNullOrWhiteSpace(s)) return ""; return HttpUtility.HtmlDecode(s); }}這樣我們就可以對字符串使用HtmlDecode這個“實例”方法了
Console.WriteLine("<textarea>你好啊,我要告訴你1>0。<textarea>".HtmlDecode());盡管擴展方法的使用會給開發者帶來效率上的極大提升,但是微軟建議只有在不得不使用擴展方法的時候才使用擴展方法,并且要謹慎地實現,因為您寫出的擴展方法完全有可能不被調用,當使用其它類庫的時候更要避免沖突。
C#為我們提供的便利遠遠不止文中提到的這么多,也不是筆者寥寥數字所能闡述完全的,.NET版本的每一次迭代都會產生更多的語法特性,亦可稱之為“語法糖”,就上面時不時提到的C#6.0而言,也還有不少其他特性,參見New features in C# 6.0。其余的各位自行探索,畢竟,我們是鐘愛.NET的,不是嗎?
新聞熱點
疑難解答