1. 程式人生 > >☕️ Write Once, Run Anywhere: 這不是 Java,這是 C#

☕️ Write Once, Run Anywhere: 這不是 Java,這是 C#

注意,本文目的並非挑起語言之爭。雖然有為C#平反之意,但主要還是介紹Mono並進行簡單的測試。

UPDATED: 25th August 2012

更新了「Compile Once, Run Anywhere:跨平臺的終極目標」一節。

© Conmajia 2012

引言

“Write once, run anywhere”(一次編寫,到處執行,WORA),有時也寫成“Write once, run everywhere”(WORE),是Sun Microsystem(於2010年被Oracle收購)為宣傳Java語言的跨平臺特性而提出的口號。在理想情況下——當然常常是不可能的——將Java語言寫成的程式編譯為標準的位元組碼(bytecode),就可以執行在支援Java虛擬機器(JVM)的任何裝置上。

很多半吊子的Java“專家”常常用這點來擠兌.NET的使用者,說他們“被微軟綁架了,只有JVM這種業界標準才能跨平臺”。

真實的情況是什麼呢?一方面,真正的Java開發者不斷抱怨著“Write once, debug anywhere"(一次編寫,到處除錯),另一方面,越來越多的人認識到.NET的本質實際是CLI/CTS,也是業界標準,CLR也是虛擬機器。所以,總是在“跨平臺”的能力上突出Java而貶低.NET,已經是落伍和壓根不懂的表現了。

最近我因為電腦執行速度慢,於是刪除了Windows,轉而安裝Linux Mint(一個基於Ubuntu的Linux發行版)。

在Linux環境下,有很出名的.NET執行時——Mono。

Mono的大名,搞.NET的朋友相信都知道。它使.NET程式在Linux下有了跨平臺執行的可能。Mono目前支援到.NET v4.0,已經逐漸趨於穩定和流行了(參見《相容性》一節)。由於我只會C#(慚愧),因此需要在Linux下開發和執行.NET程式,於是安裝Mono。

$ sudo apt-get install mono-gmcs libmono-system-data2.0-cil libmono-system-ldap2.0-cil libmono-system-messaging2.0-cil libmono-system-runtime2.0-cil

這裡說個題外話。儘管對於已經廣泛使用的技術(如.NET)而言,執行時的檔案大小已經沒有太大的討論意義,但是仍然有人拿這個說事,以此說明.NET Framework是如何如何不好(其實Win Vista之後這已經不算事了)。那麼Mono的表現又如何呢?
Mono的完全安裝大小為78MB(Java最小安裝尺寸95MB),而Mono最小化安裝之需要7MB。(參考文獻:

http://www.infoq.com/cn/news/2007/07/Mono-Runtime-Size

為了能夠方便開發,我直接安裝了MonoDevelop。這是Windows上大名鼎鼎的開源.NET IDE SharpDevelop的Linux版本。

安裝命令如下:

$ sudo apt-get install monodevelop

Linux下編譯

下面是幾個簡單的程式測試。注意,這裡的程式程式碼在Windows下是完全可以執行的。

命令列程式

 1 using System;
 2  
 3 namespace Test
 4 {
 5     class Program
 6     {
 7         static void Main()
 8         {
 9             Console.WriteLine("Hello Mono!");
10             Console.ReadLine();
11         }
12     }
13 }

執行結果

WinForm程式

 1 using System;
 2 using System.Windows.Forms;
 3 
 4 namespace test
 5 {
 6     public class MainForm:Form
 7     {        
 8         TextBox textBox1;
 9         Button button1;
10         public MainForm ()
11         {
12             textBox1=new TextBox();
13             textBox1.Text="Text here...";
14             textBox1.Location=new System.Drawing.Point(10,10);
15             button1=new Button();
16             button1.Text="Click me.";
17             button1.AutoSize=true;
18             button1.Location=new System.Drawing.Point(10,40);
19             this.Controls.Add (textBox1);
20             this.Controls.Add (button1);
21         }        
22     }
23 }

執行結果

是不是很意外?Linux下面可以直接執行WinForm的程式。就是這麼方便。演示程式碼是在Linux下編譯的,還不能證明“Write once, run anywhere”,那麼,就直接執行Windows下編譯出來的exe又如何?我們來試試編譯型程式跨平臺的終極目標:Compile once,run anywhere

Compile Once, Run Anywhere:跨平臺的終極目標

下面是我之前在Windows下用Visual Studio和SharpDevelop編譯的exe不做任何處理(也沒法處理)直接執行。

首先是《蜂巢大戰》,先來看看Windows下執行的效果。

然後是在Linux下執行。

注意:因為預設.exe是和歸檔管理器關聯的,所以需要選擇開啟方式為“Mono Runtime”。

執行效果如下

經測試各種功能正常。說明GDI+工作正常,ToolStrip等控制元件也執行正常。

再來看看我最近發表的另一個程式:《InvokeHelper》。

Windows下是這樣的

在Mono環境下執行是這個效果

說明和執行緒相關的功能工作正常。

再來是和Windows API相關的。其實用腳指頭想也是不可能的(不光C#,隨便什麼語言都一樣,這種和平臺API強相關的,怎麼可能“跨平臺”呢)。

《獲取系統圖標》,這個程式使用了SHGetFileInfo這個Windows API:

1 [DllImport("Shell32.dll")]
2 static extern int SHGetFileInfo(
3   string pszPath,
4   uint dwFileAttributes,
5   ref   SHFILEINFO psfi,
6   uint cbFileInfo,
7   uint uFlags
8 );

在Windows中工作正常

在Linux下如何呢?執行下試試:

呼叫開啟檔案對話方塊正常,但是一旦執行到Windows API就自動退出。所以,跨了平臺後,和平臺(Win)相關的API不能用了,這也是理所當然的。C#和Java都沒辦法跳掉這樣的命運(笑)。

相容性

這裡有一個例子展示了目前MONO的一些相容性情況:支援範型(2.0+)和var(3.0+)。

目前最新的Mono is 2.10.8. (Released December 19th, 2011)已經可以支援.NET 4.0版本。參見下圖:

移植

選用不同的平臺,遲早要面對移植問題。由於CLI/CTS只規定了語言的基礎部分,因此各個執行時的實現有部分差異(參見上一節:相容性)。所以Mono官方提供了一個叫做Mono Migration Analyzer(MOMA,摩碼)的移植輔助工具。這個工具可以直接告訴你將一個現成的基於Windows + Microsoft.Net的程式,移植到Win/Linux/Mac + Mono的可能性。

有時候實現一個小功能,實現方式其實有好多種,但有的實現方式是依賴於Windows API的,有的不是,在不影響效能的前提下,我們要優先選擇標準實現而不是特殊實現。這就是用Mono做專案的成功祕訣。

總結

目前比較有名的非Windows平臺下.NET虛擬機器/執行時暫時只有Mono、Portable.NET(感謝@鶴沖天),相信隨著時間推移,會有更多的Runtime出現,Mono也會變得更強大。到時,不止是Java,C#還有.NET平臺下的各種語言(VB、C++/CLI、F#等)都可以實現“Write once, run anywhere”了。當然,還有隨之而來的“Debug anywhere”(笑)。

(完)

© Conmajia 2012