- .NET 4.0面向對象編程漫談:基礎篇
- 金旭亮
- 2829字
- 2019-01-01 12:20:39
1.4 了解編程語言的發展趨勢
自從上個世紀中葉計算機出現以來,編程語言走了一條長長的發展之路。
C#之父Anders Hejlsberg在2010年所做的一個名為“C# 4.0 and Beyond”講演中介紹了他對于編程語言發展趨勢的判斷,指出了現代編程語言應該擁有的三大特性(見圖1-14)。
下面向讀者介紹一下這三大特性都包容了哪些內容。

圖1-14 現代編程語言的三大特性
1.4.1 聲明式編程風格
聲明式(Declarative)編程特性體現為告訴計算機“要干什么(Do what?)”,而非“怎么干(How to do?)”。
LINQ就是聲明式編程的典型實例,如果將它與標準的面向對象編程做個對比,就很清楚這兩者的不同了。
下面舉一個實例(參看示例項目ImperativeVsDeclarative)。
假設現在有一個Student對象集合保存了一批學生的信息:
public class Student { public string Name { get;set;} public string City { get;set;} }
需要按City屬性對其進行分組統計(圖1-15)。

圖1-15 對學生數據進行分組統計
下面先介紹一下經典的面向對象實現方法,再與LINQ做個對比。
先定義一個數據結構用于保存數據分組:
class StudentGroup { public string City; public List<Student> Students=new List<Student>(); }
編寫以下方法完成整個數據處理工作:
static void GroupStudentByCity() { var students=GetStudents();//獲取學生對象集合 var results=new List<StudentGroup>();//用于保存分組結果 //遍歷學生對象集合 foreach(Student student in students) { //是否已有此分組? StudentGroup res=results.Find( item=> item.City==student.City); if(res !=null)//有,則向現有分組追加學生 res.Students.Add(student); else//無,則新建一個分組,并且將當前學生對象加入 { StudentGroup newRes=new StudentGroup() { City=student.City }; newRes.Students.Add(student);//為新分組添加學生對象 results.Add(newRes);//添加新分組 } } PrintResult(results);//輸出結果 }
上述代碼清晰地告訴計算機:第一步做什么,第二步做什么……擁有一個明確的執行流程,并且我們可以從代碼中清楚地看到整個數據處理邏輯。
以下為使用LINQ的版本:
static void GroupStudentByCityUseLINQ() { var results=from student in GetStudents()//指定要處理的數據 group student by student.City into grp//這些數據需要分組 select ProcessGroup(grp);//對每個分組進行一些加工 PrintResult(results);//輸出結果 }
可以看到,使用LINQ編程,只需告訴計算機我們“要什么”,而讓計算機自己去決定具體的步驟。
通常,我們將那種“經典的”、需要規定好計算機具體工作步驟的編程語言稱為是“命令式(Imperative)”的,它與具有“聲明式(Declarative)”特性的編程語言對比如圖1-16所示。
在Anders Hejlsberg看來,具備聲明式編程風格是現代編程語言的一個發展方向。

圖1-16 聲明式編程風格與命令式編程風格的對比
1.4.2 動態性編程特性
所謂“動態性(Dynamic)”,其實就是將一些原本在編譯時完成的工作(比如進行類型識別、生成方法調用指令等)推遲到程序運行時才進行。
比較知名的動態編程語言包括Ruby和Python,Web開發中常用的JavaScript也可以歸入動態編程語言的范疇。
使用動態語言編程的好處主要是靈活,開發效率較高。
.NET 4.0以前,CLR是針對靜態編程語言(如C#)而設計的,無法直接運行Ruby和Python等動態語言開發的程序。
.NET 4.0引入了一個“動態語言運行時(Dynamic Language Runtime,DLR)”,在CLR之上提供了一個動態語言的運行環境,從而允許Ruby等動態語言編寫的程序在.NET平臺上運行(見圖1-17)。

圖1-17 .NET 4.0引入的“動態語言運行時(DLR)”
有趣的是,C#和VB.NET等“傳統”語言現在也可以利用DLR的功能,具備部分的動態語言特性,比如C# 4.0就引入了一個新的dynamic關鍵字用于定義“動態類型檢查”的變量,由此可以寫出以下C#代碼:
dynamic d=1;
dynamic result=d.DoSomething();
Console.WriteLine(result);
變量d保存的是一個整數,默認情況下它不可能有一個DoSomething方法,然而,上述代碼卻可以順利地通過編譯,直到運行時才報告引發了一個RuntimeBinderException異常。這說明C#編譯器“看到”dynamic類型的變量時,它不去檢查此變量的真實類型,而將對“d.DoSomething()”這個表達式的類型推斷推遲到程序運行時。
示例項目DynamicExample展示了更多的動態編程技術細節,請讀者自行閱讀。
交叉鏈接
本書第23章《邁進動態編程的世界》對C# 4.0所引入的動態編程特性和DLR的基本原理進行了系統的介紹。
1.4.3 支持并行程序的開發
隨著計算機進入多核時代,編寫支持多核并行的軟件成為潮流。因此,編程語言也要進行相應的變革,以提升并行程序的開發效率。
在.NET 4.0中,引人注目地添加了一個“并行擴展(Parallel Extensions)”,它為所有.NET編程語言提供了一個開發并行程序的平臺。在編程語言層面,主要體現為將“傳統”的LINQ增強為支持并行功能的“并行LINQ(Parallel LINQ,PLINQ)”,由于LINQ本身可與編程語言無縫集成,這實際上相當于“間接地”給.NET編程語言擴充了開發并行程序的能力。
將LINQ轉換為PLINQ非常簡單,很多情況下只需添加一個新的AsParallel子句即可,例如,我們可以很方便地將前一小節中對整數數組進行處理的LINQ查詢轉換為并行查詢:
var results=from student in GetStudents().AsParallel()
group student by student.City into grp
select ProcessGroup(grp);
上述代碼在多核CPU上運行時,會自動地劃分為多個子工作任務并行執行。其中關鍵的一點是這些代碼并不假設一定會在多核CPU上運行,也不假定CPU執行核的個數,一切都是“透明”的,用戶將會發現這些代碼在多核CPU上執行會得到更好的性能,但在單核CPU上也能得到正確的結果。
交叉鏈接
本書第19章《并行計算技術基礎》全面介紹了.NET 4.0所引入的并行擴展。.NET并行計算技術是建立于傳統的多線程開發基礎之上的,筆者建議對并行計算感興趣的讀者能通讀本書第4篇《進程、線程與并行計算》的5章(第15~19章),有助于建立起一個比較系統完整的.NET 4.0并行計算知識框架。
1.4.4 .NET編程家族新成員——F#
.NET 4.0除了在基類庫這個層面添加了對并行計算的支持之外,還引入了一種新的可以方便地開發并行程序的.NET編程語言,稱為“F#”。F#本質上是一種函數式編程語言,擁有許多對于面向對象程序員來說非常有趣的特性,比如所有的“變量”都是只讀的,廣泛采用遞歸的方式編程等。
以下F#示例代碼用遞歸的方式定義了一個函數fib,它可以求出Fibonacci數列中指定位置的數值(示例解決方案FibonacciForFS):
let rec fib x= match x with |0->0 |1-> 1 |_-> fib(x-1)+fib(x-2);;
定義好上述遞歸函數之后,“fib 10”就代表第10個Fibonacci數(它等于55)。
使用Visual Studio 2010創建一個F#項目之后,在代碼編輯器中選中要執行的代碼,按Alt+Enter鍵,就會在Visual Studio 2010下部的“F# Interactive(F#交互式)”窗口中看到其執行結果(見圖1-18)。

圖1-18 在Visual Studio 2010中開發F#應用程序
如圖1-18所示,F#的開發方式類似于早期的Basic,即時輸入,即時運行。
擴充閱讀
這種即時輸入、即時運行的開發模式有一個專門的術語,稱為“REPL”,是“Read-Evaluate-Print Loop(讀入→執行→輸出 循環迭代)”的縮寫,許多動態編程語言(比如Python)也擁有這種開發模式。在2010年微軟技術日大會上,C#之父Anders Hejlsberg介紹說他們正在致力于將編譯器的功能開放為編程語言直接可用的API,因此C# 的后繼版本(C# 5.0)將能實現C#代碼的“即時編譯”和“即時運行”,這樣一來,強類型的C#編程語言也可以像動態編程語言一樣,采用REPL的開發模式。
F#其實是一個“混血兒”,它不僅支持函數式編程風格,也能開發傳統的面向對象程序。
F#的獨特特性(比如由于所有“變量”只讀,從而在多線程環境下數據無需加鎖)使得它在并行程序開發、實現一些典型的數學算法、需要對海量數據進行的數據處理等領域大有用武之地。
F#其實是現代編程語言的一個典型范例,C#也將走向類似的道路。
技術春秋
并不新鮮的函數式編程
F#雖然在計算機編程語言歷史上是一種新的編程語言,但它所歸屬的函數式編程語言家族卻有著長達五十多年的悠久歷史。舉個例子,1977年出現的FP就是一種函數式編程語言,它的設計者是John Warner Backus。
這里說說John Warner Backus,他是一名成就卓越的計算機科學家,設計了人類第一個高級程序設計語言Fortran,還發明了著名的“巴克斯范式(Backus Normal Form,BNF)”,它的擴充版本(Extended BNF,EBNF)是當前描述各種程序設計語言語法的常用工具。
所以,許多看上去很“新”的東西其實“很舊”,新路是接在老路后頭的。