在 .NET 平台寫程式寫久了,一定會覺得程式中的資料一定得物件化,而且要有明確的成員來規範,沒錯,這是我們告訴大家寫程式的基本要求,能做強型別的就一定要做強型別,不然光是轉型 (type casting) 這件事就能搞死一堆人了...
在 .NET 平台寫程式寫久了,一定會覺得程式中的資料一定得物件化,而且要有明確的成員來規範,沒錯,這是我們告訴大家寫程式的基本要求,能做強型別的就一定要做強型別,不然光是轉型 (type casting) 這件事就能搞死一堆人了。
然而,卻不是所有的情況都得用強型別,尤其是當資料只會用一次,在一個地方使用,或是想要更具彈性時,宣告成強型別會大大的縮減它的彈性,但我們又不想要使用弱型別的物件 (ex: ArrayList, DataTable, DataRow, …),這時 .NET 的匿名型別 (anonymous type) 就很有用了,它的型別資訊是由編譯器在編譯過程中動態給予的,開發人員只要用很簡單的 new 就能自由創造匿名型別。
不過,創造歸創造,我們如何在程式中使用這些匿名物件呢?因為匿名物件有幾個特性:
- 匿名物件擁有自己的型別,無法轉型成其他型別,只有 object 除外。
- 匿名物件只能存在於參數,無法跨越出函數之外,除非回傳的是 object。
- 匿名物件宣告後,只能使用 dynamic 或 Reflection 來存取。
掌握了這幾個特性後,我們可以做一些設計。
例如下列程式碼是一個將兩位數相加的程式,但參數並沒有給予要相加的兩個值,我們要以匿名物件來宣告:
public static object AddWithObject(object Params)
{
PropertyInfo[] ps = Params.GetType().GetProperties();
object result =
Convert.ToInt32(ps[0].GetValue(Params, null)) +
Convert.ToInt32(ps[1].GetValue(Params, null));
return result;
}
當 Params 傳入的是匿名型別時,我們因為無法使用強型別的方式來存取,因此我們只能使用 Reflection 將 Params 的匿名型別內的屬性反射出來,才能得到我們需要的資訊。用戶端程式可以這樣撰寫:
var o2 = AddWithObject(new
{
X = 200,
Y = 392
});
這樣做也許有人會覺得麻煩,我們可以利用 .NET 4.0 提供的 dynamic 算符來簡化,dynamic 算符會要求 CLR 在執行時期才繫結型別資訊,也就是說我們可以不用 Reflection 即可存取匿名物件內的成員,我們可以把 AddWithObject() 改變寫法使用 dynamic:
public static dynamic AddWithDynamic(dynamic Params)
{
return Params.X + Params.Y;
}
用戶端程式可以這樣寫:
var o1 = AddWithDynamic(new
{
X = 100,
Y = 140
});
結果和 AddWithObject() 是相同的。
不過我個人是推薦 AddWithObject 的寫法,因為它比較彈性,我們還可以先一步偵知指定的屬性是否存在,而 dynamic 的寫法無法偵知指定的屬性是否存在,例如我們把 AddWithDynamic 中的參數 Y 拿掉,在執行時期 CLR 會丟給你一個 RuntimeBinderException,因為找不到 Y 屬性。
但如果已經確定匿名型別一定會傳入哪些屬性的話,使用 dynamic 可以簡化不少程式。
完整範例程式:
namespace AnonymousTypeSample
{
class Program
{
static void Main(string[] args)
{
var o1 = AddWithDynamic(new
{
X = 100
//Y = 140
});
var o2 = AddWithObject(new
{
X = 200,
Y = 392
});
Console.WriteLine("Add1: {0}", o1);
Console.WriteLine("Add2: {0}", o2);
Console.ReadLine();
}
public static dynamic AddWithDynamic(dynamic Params)
{
return Params.X + Params.Y;
}
public static object AddWithObject(object Params)
{
PropertyInfo[] ps = Params.GetType().GetProperties();
object result =
Convert.ToInt32(ps[0].GetValue(Params, null)) +
Convert.ToInt32(ps[1].GetValue(Params, null));
return result;
}
}
}
Reference: http://msdn.microsoft.com/zh-tw/library/bb397696.aspx