2010年2月27日 星期六

程式語言趨勢

長久以來,各家 programming language 都會標榜自家的語言多好,其他語言將如何如何。其實,語言只是工具。語言是否能成為主流,要看大環境、使用的平台、廠商是否支持、原廠是否持續力推等等。

TIOBE Programming Community Index for February 2010 這裡的調查結果發現,Java 仍然是使用率最高的語言。Java 已死的說法不盡然。

我喜歡看趨勢,而非一時的高低。在該資料中,也可發現 Java 長期來,由2001年的 27% 下降到 2010年的17.5%。同一時間的C# 由 0.1% 不斷地上升到 5%, PHP 由 1.5% 上升到 10.0%。若由此觀察,Java 語言不斷地流失市佔率也是事實。

而另一個事實,靜態語言與動態語言的比例趨勢,看來動態語言是大幅提升到了不可忽視,甚至必需學習的地步。到目前為止,動態佔6成,靜態佔4成。由此也可了解微軟後來為何出了 F#語言,支援IronPython, IroRuby等動作。

2010年2月24日 星期三

讀取大型文字檔

以往在讀取文字檔時,會使用 File.ReadAllLines 方法,一次取得所有行。如下

static void Main(string[] args)
{
  string[] lines = File.ReadAllLines("TextFile1.txt");
  foreach (var line in lines)
    Console.WriteLine(line);
}

相當簡單。但這樣的方法,遇到大型文字檔,例如100MB時,就很麻煩了。原因在ReadAllLines 時就會一次將所有的資料讀到 lines 中,可能造成記憶體不足。

在 c# 2.0 時,介紹了 yield 的語法。在 c#3.0 時,介紹了 LINQ 語法。於是上述的問題就可以解了。如下

static void Main(string[] args)
{
  //string[] lines = File.ReadAllLines("TextFile1.txt"); // may need large memory
  var lines = ReadLines("TextFile1.txt");  //not consume large memory
  foreach (var line in lines)
    Console.WriteLine(line);
}

public static IEnumerable<string> ReadLines(string filePath)
{
  using (StreamReader reader = new StreamReader(filePath))
  {
    while (!reader.EndOfStream)
      yield return reader.ReadLine();
  }
}

上述的寫法很好。適合寫成常用的 framework! 因此,在 .net framework 4.0 中,已經將該段程式新增為 File.ReadLines

static void Main(string[] args)
{
  //string[] lines = File.ReadAllLines("TextFile1.txt"); // may need large memory
  var lines = File.ReadLines("TextFile1.txt"); // not need large memory, new in .net framework 4.0
  foreach (var line in lines)
    Console.WriteLine(line);
}

 

類似新增的方法,有

File.WriteAllLines, File.AppendAllLines, Directory.EnumerateDirectories, Directory.EnumerateFiles, Directory.EnumerateFileSystemEntries, DirectoryInfo.EnumerateDirectories, DirectoryInfo.EnumerateFiles, DirectoryInfo.EnumerateFileSystemInfos

2010年2月12日 星期五

C# 4.0: dynamic language (3)

前例中,我們使用了 ExpandoObject 做為動態物件,但 ExpandoObject 卻是不可被繼承的(sealed)。前例中的 Show 函式在執行時期被指定的。

   1: dynamic o = new ExpandoObject();
   2: o.Name = "aaa";
   3: o.Id = 3;
   4: o.Show = new Action(() => Console.WriteLine("Id = " + o.Id + ", Name = " + o.Name));
   5: o.Show();

如果,這樣的行為也可以有物件導向的行為就好了。這樣一來,Show函式就是固定的行為,而不是臨時被指定的,畢竟OO的概念在軟體重用上佔了一席之地,也是我們習慣的設計原則了。

於是,DynamicObject 就出現了。DynamicObject 透過繼承,將內部的物件以動態的方式呈現出來。當存取不存在的屬性、方法時,就會呼叫 DynamicObject 的 TrySetMember, TryGetMember, TryInvokeMember 等函式。

以下,就將上例改成 DynamicObject 來實作一次。

   1: class Program
   2: {
   3:   static void Main(string[] args)
   4:   {
   5:     dynamic o = new MyObj();
   6:     o.Name = "aaa";
   7:     o.Id = 3;
   8:     o.Show();
   9:   }
  10: }
  11:  
  12: class MyObj : DynamicObject
  13: {
  14:   Dictionary<string, object> _innerDictionary = new Dictionary<string, object>();
  15:   public override bool TrySetMember(SetMemberBinder binder, object value)
  16:   {
  17:     _innerDictionary[binder.Name] = value;
  18:     return true;
  19:   }
  20:   public void Show()
  21:   {
  22:     Console.WriteLine("Id = " + _innerDictionary["Id"] + ", Name = " + _innerDictionary["Name"]);
  23:   }
  24: }

由新版的例子來看,Show 函式已經是固定的行為了,一定會取得 Name 及 Id 的資料出來顯示。

DynamicObject 在行為了更為奇特,它具有動態物件的特性,卻保有繼承的概念。其實,動態語言也是可以繼承的,只是我們寫靜態語言習慣了,以為繼承是靜態語言的特性罷了。

2010年2月11日 星期四

C# 4.0: dynamic language (2)

Javascript 是一個動態語言。請看下面這一段 script

   1: <script type="text/javascript">
   2:     function Test() {
   3:       var o = new Object();
   4:       o.Name = "aaa";
   5:       o.Id = 3;
   6:       o.Show = function () { alert("Id = " + o.Id + ", Name = " + o.Name) };
   7:       o.Show();
   8:     }
   9:     Test();
  10:   </script>

而 C# 4.0 呢?有沒有相對應的寫法呢?有的!將 var 改成 dynamic,而 Object 改成 ExpandoObject。而沒有回傳值的函數,就是 Action。如下。

   1: static void Main(string[] args)
   2:     {
   3:       dynamic o = new ExpandoObject();
   4:       o.Name = "aaa";
   5:       o.Id = 3;
   6:       o.Show = new Action(() => Console.WriteLine("Id = " + o.Id + ", Name = " + o.Name));
   7:       o.Show();
   8:     }

是不是很類似呢?原來,C# 3.0時, lamda expression 就是採用了動態語言的特性,但使用靜態語言的編譯器就達到了動態語言的效果。

C# 4.0 : dynamic language (1)

在 C# 4.0,增加了動態語言的特性。

簡單地說,使用 dynamic 宣告出來的變數,在編譯器編譯時期會被省略不編譯,直到執行期到動態地解譯所使用的 member field,  member function 或 function。

舉例來說,下面的程式會發生編譯錯誤。原因是 object 這種類別沒有 Name 的屬性。

例1
   1: class C1
   2: {
   3:   public string Name { get; set; }
   4: }
   5: class Program
   6: {
   7:   static void Main(string[] args)
   8:   {
   9:     C1 c1 = new C1() { Name = "a" };
  10:     WriteName(c1);
  11:   }
  12:   public static void WriteName(object o)
  13:   {
  14:     Console.WriteLine(o.Name);
  15:   }
  16: }

想要讓這個程式碼 compile 通過當然是很容易的事,只需要將 WriteName 的參數由 object 型別改成 C1 型別即可。

以下這一段程式,想要寫一個能印出物件的 Name 屬性的共用程式 WriteName。

例2
   1: class C1
   2:   {
   3:     public string Name { get; set; }
   4:   }
   5:   class C2
   6:   {
   7:     public string Name { get; set; }
   8:     public int Id { get; set; }
   9:   }
  10:   class Program
  11:   {
  12:     static void Main(string[] args)
  13:     {
  14:       C1 c1 = new C1() { Name = "a" };
  15:       C2 c2 = new C2() { Name = "b", Id = 1 };
  16:       WriteName(c1);
  17:       WriteName(c2);
  18:     }
  19:     public static void WriteName(C_What o)
  20:     {
  21:       Console.WriteLine(o.Name);
  22:     }
  23:   }

WriteName 的參數1,應該宣告 C1 還是 C2 呢?都不行,靜態語言是不會過的。精通物件導向的我們,當然知道此時最好使用 interface 來解決問題。如下。

例3
   1: interface IName
   2:   {
   3:     string Name { get; set; }
   4:   }
   5:   class C1 : IName
   6:   {
   7:     public string Name { get; set; }
   8:   }
   9:   class C2 : IName
  10:   {
  11:     public string Name { get; set; }
  12:     public int Id { get; set; }
  13:   }
  14:   class Program
  15:   {
  16:     static void Main(string[] args)
  17:     {
  18:       C1 c1 = new C1() { Name = "a" };
  19:       C2 c2 = new C2() { Name = "b", Id = 1 };
  20:       WriteName(c1);
  21:       WriteName(c2);
  22:     }
  23:     public static void WriteName(IName o)
  24:     {
  25:       Console.WriteLine(o.Name);
  26:     }
  27:   }

這樣一來,解決了 C1, C2 的問題。「只」需要實作 IName 的介面,就可以使用 WriteName 這個函數了。

問題就在 IName 是一個自訂的介面,不是通用的介面。別人寫好的類別,絕對不會知道及實作我們自訂的 IName 介面。如果我們的WriteName 想要通吃呢?下面的程式就是使用 Reflection 機制寫出來的 solution

例4
   1: class C1
   2:   {
   3:     public string Name { get; set; }
   4:   }
   5:   class C2
   6:   {
   7:     public string Name { get; set; }
   8:     public int Id { get; set; }
   9:   }
  10:   class Program
  11:   {
  12:     static void Main(string[] args)
  13:     {
  14:       C1 c1 = new C1() { Name = "a" };
  15:       C2 c2 = new C2() { Name = "b", Id = 1 };
  16:       WriteName(c1);
  17:       WriteName(c2);
  18:     }
  19:     public static void WriteName(object o)
  20:     {
  21:       Type type = o.GetType();
  22:       string str = (string)type.GetProperty("Name").GetValue(o, null);
  23:       Console.WriteLine(str);
  24:     }
  25:   }

這個方式,一律使用 object 來接參數,再使用 Reflection 的機制來動態的取得 Name 的 property,並當成字串輸出到 Console。棒吧!

可是,這段程式有點難寫耶!如果一次要取得三個property,並執行傳入參教的某個 function,就累翻了。

最後,則是使用 dynamic 的宣告,將例3的 C_What 改成 dynamic 即完成了

例5

   1: class C1
   2:   {
   3:     public string Name { get; set; }
   4:   }
   5:   class C2
   6:   {
   7:     public string Name { get; set; }
   8:     public int Id { get; set; }
   9:   }
  10:   class Program
  11:   {
  12:     static void Main(string[] args)
  13:     {
  14:       C1 c1 = new C1() { Name = "a" };
  15:       C2 c2 = new C2() { Name = "b", Id = 1 };
  16:       WriteName(c1);
  17:       WriteName(c2);
  18:     }
  19:     public static void WriteName(dynamic o)
  20:     {
  21:       Console.WriteLine(o.Name);
  22:     }
  23:   }

例5 的語法與例3幾乎完全一樣,但事實上走的是例4的執行時間Reflection 機制。大大地節省了程式碼的撰寫。

由執行時期才決定資訊的機制,就是動態語言的特性。

2010年2月9日 星期二

VS2010 RC 今天出來了

今天 (2010/02/09)出來的,只能從英文版的 msdn subscriptions。繁體中文件介面還看不到。

正式版預計 4/12 出來。

後記:(2010/02/12)
公開下載的位置,資訊更完整,連同 Visual Studio 2010 SDK 等也一起出來公開下載了。

2010年2月6日 星期六

MVC 2.0 RC2 又出來了

沒多久前,才出了 RC 版,現在又出了 RC2 了。

版本說明請見 haacked 的 blog

Share with Facebook