既然要動態就動個痛快 - ExpandoObject
| | | 0 | |
動態語言是C# 4.0的重要特色之一,dynamic關鍵字的出現,簡化了以往用Reflection大費周章才能做到的物件屬性(Property)及方法(Method)動態存取。
用個簡單的例子示範:
using System; using Microsoft.CSharp.RuntimeBinder; using System.Reflection; namespace DynamicLab { //定義兩個類別,有一個相同的Property(Name) //但各有不同的Field(Age vs Score) class Boo { public string Name { get; set; }
public int Age;
}
class Foo { public string Name { get; set; }
public int Score;
}
class Program { static void Main(string[] args)
{ //分別建立兩個物件 Boo boo = new Boo() { Name = "Jeffrey", Age = 25 }; Foo foo = new Foo() { Name = "Darkthread", Score = 32767 }; //使用Reflection存取Name屬性及欄位Score ShowAnythingByReflection(boo);
ShowAnythingByReflection(foo);
//改用dynamic來處理做為對照 ShowAnythingByDynamic(boo);
ShowAnythingByDynamic(foo);
Console.Read();
}
#region Reflection //Reflection法 static void ShowAnythingByReflection(object obj)
{ Console.WriteLine("Type: " + obj.GetType().ToString()); Console.WriteLine("Name=" + TryGetProperty(obj, "Name"));
try { Console.WriteLine("Score=" + TryGetField(obj, "Score"));
}
catch (Exception ex) { Console.WriteLine("Error:" + ex.Message); }
}
//處理Property與處理Field做法不同,故寫成兩個Method static object TryGetProperty(object obj, string propName)
{ Type objType = obj.GetType();
PropertyInfo pi = objType.GetProperty(propName);
if (pi == null)
throw new ApplicationException(
"Property " + propName + " not found!");
else return pi.GetValue(obj, null);
}
static object TryGetField(object obj, string fieldName)
{ Type objType = obj.GetType();
FieldInfo fi = objType.GetField(fieldName);
if (fi == null)
throw new ApplicationException(
"Field " + fieldName + " not found!");
else return fi.GetValue(obj); }
#endregion #region Dynamic //dynamic比較乾脆,直接串上.propName或.fieldName就對了 //編譯一定會過(因為不檢查),若Property或Field不存在, //則丟出RuntimeBinderException static void ShowAnythingByDynamic(dynamic dyna)
{ Console.WriteLine("Type: " + dyna.GetType().ToString()); try { Console.WriteLine("Name=" + dyna.Name); }
catch (RuntimeBinderException ex) { Console.WriteLine("Error:" + ex.Message); }
try { Console.WriteLine("Score=" + dyna.Score); }
catch (RuntimeBinderException ex) { Console.WriteLine("Error:" + ex.Message); }
}
#endregion }
}
執行結果:
Type: DynamicLab.Boo
Name=Jeffrey
Error:Field Score not found!
Type: DynamicLab.Foo
Name=Darkthread
Score=32767
Type: DynamicLab.Boo
Name=Jeffrey
Error:'DynamicLab.Boo' does not contain a definition for 'Score'
Type: DynamicLab.Foo
Name=Darkthread
Score=32767
面對傳入可能是Boo或Foo的類別,試圖存取其Name屬性及Score欄位,用dynamic寫是否比用Reflection乾淨俐落多了?
不過,在以上的例子中,Boo、Foo都是事先定義好的類別,並沒法真的做到像Javascript的變形蟲玩法,在Runtime才動態決定掛上哪些Property、Method:
var boo = {}; boo["Name"] = "Jeffrey";
alert(boo.Name);
boo["Show"] = function(m) { alert(m); };
boo.Show("Hello, World!"); 會這樣提,莫非.NET 4.0現在也辦得到?
Yes! Use the ExpandoObject, Luke!
using System; using Microsoft.CSharp.RuntimeBinder; using System.Reflection; using System.Dynamic; using System.Linq; using System.Collections.Generic; namespace DynamicLab { class Program { static void Main(string[] args)
{ //將boo建立成ExpandoObject dynamic boo = new ExpandoObject(); //直接寫boo.Name加上新的Property boo.Name = "Jeffrey"; Console.WriteLine(boo.Name);
//ExpandoObject可轉型成IDictionary<stirng, object> //讓我們可以用boo[someVariable]的方式加上新的成員 //在要動態決定物件成員名稱的場合很好用 IDictionary<string, object> booDict =
boo as IDictionary<string, object>;
//掛上Show方法 booDict["Show"] = (Action<string>)
(m => { Console.WriteLine(m); }); boo.Show("Hello, World!"); Console.Read();
}
}
}
很酷吧!
動態語言寫起來很簡潔,但卻失去了編譯期語法檢查的保護,導致在執行期可能發生料想不到的錯誤,該不該用見仁見智,但每一種語言特性一定有其擅長的場合,要怎麼巧妙運用就看開發者的功力。至少,C# 4.0提供了十八般武器,要用小李飛刀、魚腸劍還是青龍偃月刀,就是大家的自由囉~~~
(話說回來,選錯兵器上場,被人砍到仆街也是常見的結果,擁用選擇的權利是好事,但也務必謹慎為之)
【延伸閱讀】
Comments
Be the first to post a comment