.net 4.0添加了dynamic關鍵字。通過聲明dynamic變量,我們可以在C#這個靜態語言中使用一下動態語言的特性。微軟添加dynamic關鍵字,主要是為了使在C#中對COM編程更加簡化。對於我這個2003年才開始學編程的人來說COM就和匯編一樣,只有聽說過的份。那麼dynamic還有什麼其他的用途呢?
最近有點時間研究了一下,感覺很好玩,很Cool。作為學習成果,下面是一個通過IDynamicMetaObjectProvider實現的一個可以混合動態Property和靜態Property的類:
public class DynamicDomainObject : IDynamicMetaObjectProvider { private Dictionary_internalPropertyStorage; public DynamicDomainObject() { _internalPropertyStorage = new Dictionary (); } public Object SetProperty(String key, Object value) { if (_internalPropertyStorage.ContainsKey(key)) { _internalPropertyStorage[key] = value; } else { _internalPropertyStorage.Add(key, value); } return value; } public Object GetProperty(String key) { if (_internalPropertyStorage.ContainsKey(key)) { return _internalPropertyStorage[key]; } return null; } public void Dispose() { _internalPropertyStorage.Clear(); _internalPropertyStorage = null; } public DynamicMetaObject GetMetaObject(Expression parameter) { return new DynamicDomainMetaObject(parameter, this, GetType()); } private class DynamicDomainMetaObject : DynamicMetaObject { private Type _type; internal DynamicDomainMetaObject( Expression parameter, DynamicDomainObject value, Type type) : base(parameter, BindingRestrictions.Empty, value) { this._type = type; } public override DynamicMetaObject BindConvert(ConvertBinder binder) { var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); if (binder.ReturnType.IsAssignableFrom(_type)) return new DynamicMetaObject(Expression.Constant(Value), restrictions); else return new DynamicMetaObject(Expression.Default(binder.ReturnType), restrictions); } public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); var propertyInfo = _type.GetProperties().FirstOrDefault(p => p.Name == binder.Name); var self = Expression.Convert(Expression, LimitType); Expression target; if (propertyInfo == null) { target = Expression.Call( self, typeof(DynamicDomainObject).GetMethod("GetProperty"), new Expression[] { Expression.Constant(binder.Name) } ); target = FixReturnType(binder, target); } else { target = Expression.Property(self, propertyInfo); target = FixReturnType(binder, target); } return new DynamicMetaObject(target, restrictions); } public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { var propertyInfo = _type.GetProperties().FirstOrDefault(p => p.Name == binder.Name); var restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType); var self = Expression.Convert(Expression, LimitType); Expression setCall; if (propertyInfo == null) { var argument = Expression.Convert(value.Expression, typeof(Object)); setCall = Expression.Call(self, typeof(DynamicDomainObject).GetMethod("SetProperty"), new Expression[] { Expression.Constant(binder.Name), argument }); } else { var argument = Expression.Convert(value.Expression, propertyInfo.PropertyType); setCall = Expression.Call(self, propertyInfo.GetSetMethod(), new Expression[] { argument }); } return new DynamicMetaObject(Expression.Block(setCall, Expression.Default(binder.ReturnType)), restrictions); } private static Expression FixReturnType(DynamicMetaObjectBinder binder, Expression target) { if (target.Type != binder.ReturnType) { if (target.Type == typeof(void)) { target = Expression.Block(target, Expression.Default(binder.ReturnType)); } else if (binder.ReturnType == typeof(void)) { target = Expression.Block(target, Expression.Empty()); } else { target = Expression.Convert(target, binder.ReturnType); } } return target; } public override IEnumerable GetDynamicMemberNames() { return _type.GetProperties().Select(p => p.Name); } } }
在這個類中,我們通過一個Dictionary來存放動態訪問的Property,而當訪問靜態Property時,則直接訪問。在這個設計中,我沒有直接使用DynamicObject而是自己實現了IDynamicMetaObjectProvider,這樣做是因為在我們日常編程中,很多時候我們的類是需要繼承其他業務類的,對於C#和Java這種單繼承的語言來說,我們要盡量把父類留給業務需要。
如何使用這個類的例子,請參考這個類的測試代碼,按照慣例,我已將代碼發布到Github上了(https://github.com/mcai4gl2/dynamic-object)。至於在平常工作上這個類會有什麼用途,那就要看各位的需要了。