近日M#的消息令江湖再次起了波瀾。大家知道,.NET已經進入了瓶頸期。這個消息又讓偶有所期待,趁此機會發表一下個人的展望,對C#或者其繼任者,不管是M#還是X#。
一、語法特性
1. using引入類型
using關鍵字在.NET中有兩個意義,這里是指引入命名空間。要是支持using類型會帶來很大方便,比如:
using System.Runtime.InteropServices.Marshall; //可以直接調用Marshall類的靜態方法和屬性 using List<string>= StringList; //寫起來不用再打<>
2. 變量命名空間
有時候,處理業務很復雜,即使在一個不能再分割的處理單元中也有許多變量,命名和組織成了一個頭疼的問題,將這些封裝成類又不值得。如果可以在聲明變量或字段時定義命名空間,這個煩惱就能解決:
class ProductUtil{ namespace Price { float old; float New; float standard; } float GetPrice(){ float Price.discount; //聲明折扣 //計算價格... return Price.New; } }
命名空間可以在方法內,也可以在類型內。聲明命空間后,可以單獨聲明一個變量。
3. 用out聲明變量.
有點經驗的開發者會經常用Try-Parse避免產生異常,一般TryParse、TryGet這類方法都把結果傳給用out聲明的參數中,我總是在想,為什么這個參數要先聲明呢?
int value; //這個聲明完全可以去掉,看起來不爽 if (int.TryParse(s, out value)) { Console.WriteLine(value); }
4. 省略屬性字段
C# 3.0推出了簡化屬性的方式:public string Text { get;set; }。但還不徹底,對於get set內部有一定處理邏輯的,無法省略字段。如果可以用一個關鍵字代替字段,將使代碼更整潔。
我覺得用default在屬性內表示字段不錯,如果在內部其他方法中要直接訪問此字段(應該很謹慎地),可以用default(this.Text),好像有點麻煩,但這種情況很少,這樣用會更醒目。
5. 能做就做
要說最煩的,還是這種判斷代碼
if(list != null && list.Count > 0){ return list.Last(); }
很多時候,根本無需處理list為null或空的情況,如果上面的代碼能可以寫成這樣,必然很受歡迎:
return list.Last()??;
從此以后,我們可以唱:兄弟你大膽地往下寫喲~~,別怕異常~~
6. 帶參構造函數約束
泛型約束只能聲明無參構造函數,這種限制沒任何道理。應可以像下面這樣。
public class MyClass<T> where T : class, new(int) { ... }
或者不限制構造函數的參數:
public class MyClass<T> where T : class, new { ... }
如果調用了不兼容的構造函數,IDE完全可以在編譯時檢查出來。
7. case可跟表達式
剛學編程接觸switch case時,就很不解case后面只能跟常量的限制。這其實應該是個bug,包括其他語言。
8. 擴展屬性
當時擴展方法推出的時候,為什么沒有擴展屬性呢?我知道肯定有某種理由,但這個真可以有。
9. 綁定運算符
對於兩個類實例 A=B 將使AB指向同一個引用,若隨后B引用變化了,對A沒有影響。許多初學者搞不清=運算符的作用,以為B變化,A也隨之變化。
其實這種特性是C#一直都欠缺的,如果我們可以讓 A==B 實現A和B的綁定(左邊可以是表達式),那許多工作就輕松了。
要考慮GC的問題,如果A未被回收,B也不能被回收。
10. 項目引用Native dll
如今要調用C/C++的程序集,必須查好參數,轉化類型,再像這種方式聲明:
[DllImport("User32.dll", CharSet = CharSet.Unicode)] public static extern int MessageBox(int h, string m, string c, int type);
為什么不能像引用.NET dll那樣引用C/C++寫的dll呢,或者像Service Reference那樣,增加一個Native Reference的目錄,自動生成代理類。
11. Fluent語法
利用語法糖,希望能夠大幅減少關鍵字使用,比如對於(int)value, value as string都可以用ToSomeType()表示。若調用構造函數,可以省略new關鍵字。
在使用第一條using聲明調用靜態方法的類后,可以將靜態方法作為任一參數的擴展方法。
using String; static void SortDates(string dates) { var lstDates = dates.Split(',').Select(d=>d.ToDateTime()); //ToDateTime()相當於(DateTime) var endDate = DateTime(lstDates.Max().Year, 12 ,31); //省略new dates = lstDates.OrderBy(d=>d).Join(","); //String.Join方法變擴展方法 }
12. 全局性配置
這其實是由Visual Studio實現的功能。如果實現擴展屬性,直接調用類靜態方法,會導致自動提示里擁擠不堪,讓便利性打折扣。應該可以在項目屬性里配置,導入一個配置模板,默認隱藏掉那些不常用的成員,比如所有類型都甩不掉的Equals/ReferenceEquals/GetHashCode。這個模板還可以用using為類型起別名,就不必在每個文件中一一聲明了。
這樣C#關鍵字會大大減少,還可以定制更加符合個人或團隊習慣的命名。
這幾天又想到兩個特性,補充一下(1-11)
13. 真正的”轉到定義“
F12轉到方法的定義,多數時候很方便,但有兩個很不方便的問題。一個是方法被子類override時,F12會轉到父類的方法,這在開發時也就罷了,調試時VS完全知道真正調用的方法是否被覆寫過,應該轉到真正調用的方法上去。還有一個是客戶端調用WCF或Service代理類的方法,F12不應轉到代理類中,應該轉到項目中WCF或Service方法的定義,除非是第三方的服務。查找引用時,也應該將Server/Client端代碼一並包括。
14. 程序集自定義引用
引用一個程序集時,應該像安裝程序一樣,可以定義引用程序集哪些模塊,這樣就可以大大減小安裝包和程序的體積。別說這是異想天開,CIL本身就支持Module的概念,只是現在編譯器偷懶沒有實現而已。
二、動態支持
C#的反射雖然很完善了,但性能還是差,並且相當不友好。如果要運行時修改代碼,即使用最先進的Lamda Expression,還有第三方框架MonoCecil都很麻煩,更別說用Emit和Code Generator。所以C#基本上還是一門靜態語言,這樣滿足諸如AOP這種高擴展性開發需求,仍然十分吃力。
我們早對一個基於項目的元數據框架望眼欲穿了,徹底解放被元數據和動態代碼束縛已久的生產力。
首先,要增強T4模板,使之更方便地引用自身和第三方程序集。實現像Razor那種語法支持應該不過分吧。使用好T4模板,可以大大提高代碼重用性(對AVL樹和紅黑樹的個人理解),還有避免元數據操作(性能相差7千倍的ToString方法)。
對於一個項目,應該提供一個編譯選項,可以自動生成強類型的元數據程序集。比如MVC中,可以這樣用:
using MySite.Metadata; //引入自動生成的元數據程序集 namespace MySite.Controllers { public class HomeController : Controller { public ActionResult Home() { if (!User.Identity.IsAuthenticated) return Views.Login; //Views自動生成,取代 View("Login") return View(); } } }
假設有一個Person類,有Name/Sex/Birthday三個屬性,我們希望這樣運行時加一個Age屬性:
MyProject.Metadata.Class.Person.CreateProperty("Age", p=> (DateTime.Now - p.Birthday).Year);
調用這些動態屬性時,只要用dynamc特性即可(當前這個特性應用很有限),最好能為dynamic類型提供一個類似JQuery的attr函數。 如果能實現JS的eval函數的功能就更好了,那樣C#代碼也許會變得我都不認識了。
三、設計面向
編程語言發展已經超過半個世紀了,先是面向變量的匯編語言,高級語言出現后,從Pascal/C語言面向過程和變量,到C++以后的面向對象。 C#和Java只是語言特性上有大幅改進,設計思想並沒有飛躍。
C#作為最為先進的編程語言,反映了當前語言發展的瓶頸。要有所突破,必須要有新的設計思想,把面向粒度提高到新的層次。
個人分析后認為,未來C#或C#的后繼者,會向三個方向發展:
1. 面向集合
未來編程語言遇到的業務邏輯將更復雜,對集合處理是業務邏輯的核心內容。LINQ使C#走在業界的前列,然而還有許多問題。
由於歷史原因,集合類型太多太亂。支持泛型是必須,我們需要根據可變性、排序性、Hash特性、並發要求等,使用一致的高性能集合類型。這些集合類能夠靈活轉化,智能地處理擴容、復制等底層操作,且沒有LINQ那樣無法跨程序域傳遞的限制。這需要框架和CLR雙重支持。
2. 面向並發
這個是很自然的方向,除非出現光、生物、量子計算技術的飛躍,不然半導體電路處理器單核極限愈來愈難突破,多核趨勢愈演愈烈。未來的編程語言,並發支持必須融入其底層。
還是拿Node.js來說,已經初具此特性,其對IO的訪問全部非阻塞的,是從底層支持的異步操作。
對於C#來說,就不只是框架上修修補補,而是CLR的全面支持。async和await出現是個很好的苗頭,期待看到更多這樣的發展。
3. 面向任務
Node.js它通過事件輪詢(event loop)來實現並行操作,這只能處理最簡單地多任務同步。要實現真正的並發語言,並滿足日益復雜的業務邏輯處理,必須對並發的單元-任務進行有力的支持。
.NET對任務有了System.Threading.Tasks下一系列類的支持,但這只是開始。我們需要動態地創建、分解、修改、取消任務,需要方便地獲取和控制任務的狀態,管理超時和資源,統計任務效率,處理異常。
前面的路還很遠,很長,也一定很精彩。以后我還會繼續研究這些方向。
四、平台支持
M#既然能開發操作系統,那定是能編譯成Native Code。其實C#本來早就可以有這個功能了,把編譯器和NGEN結合一下就行了。但我倒覺得即使有也不沒有什么激動人心的,畢竟操作系統才幾個?接觸的人屈指可數。何況微軟一個新操作系統未來能贏得多少市場也未敢期望太高。重要一百倍的平台支持,我認為是各種瀏覽器IE/FF/Chrome,或者三大瀏覽器引擎。
其次,至少微軟自家的產品IE應該支持,還有SQL Server/Office這些重量級產品,不支持實在說不過去。
我曾經以為Javascript會在數年被淘汰,然而現在都開始逆襲Server端了。如果C#開發的程序能直接在各種瀏覽器上運行,C#才真正迎來了春天。