.net 4.0添加了dynamic关键字。通过声明dynamic变量,我们可以在C#这个静态语言中使用一下动态语言的特性。微软添加dynamic关键字,主要是为了使在C#中对COM编程更加简化。对于我这个2003年才开始学编程的人来说COM就和汇编一样,只有听说过的份。那么dynamic还有什么其他的用途呢?
最近有点时间研究了一下,感觉很好玩,很Cool。作为学习成果,下面是一个通过IDynamicMetaObjectProvider实现的一个可以混合动态Property和静态Property的类:
public class DynamicDomainObject : IDynamicMetaObjectProvider
{
private Dictionary<String, Object> _internalPropertyStorage;
public DynamicDomainObject()
{
_internalPropertyStorage = new Dictionary<String, Object>();
}
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<string> GetDynamicMemberNames()
{
return _type.GetProperties().Select(p => p.Name);
}
}
}
在这个类中,我们通过一个Dictionary来存放动态访问的Property,而当访问静态Property时,则直接访问。在这个设计中,我没有直接使用DynamicObject而是自己实现了IDynamicMetaObjectProvider,这样做是因为在我们日常编程中,很多时候我们的类是需要继承其他业务类的,对于C#和Java这种单继承的语言来说,我们要尽量把父类留给业务需要。
如何使用这个类的例子,请参考这个类的测试代码,按照惯例,我已将代码发布到Github上了(https://github.com/mcai4gl2/dynamic-object)。至于在平常工作上这个类会有什么用途,那就要看各位的需要了。