Duck Typing DynamicObject衍生
我写了一个类,允许派生指定哪些属性可以延迟加载。该代码是:Duck Typing DynamicObject衍生
public abstract class SelfHydratingEntity<T> : DynamicObject where T : class {
private readonly Dictionary<string, LoadableBackingField> fields;
public SelfHydratingEntity(T original) {
this.Original = original;
this.fields = this.GetBackingFields().ToDictionary(f => f.Name);
}
public T Original { get; private set; }
protected virtual IEnumerable<LoadableBackingField> GetBackingFields() {
yield break;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
result = field.GetValue();
return true;
} else {
var getter = PropertyAccessor.GetGetter(this.Original.GetType(), binder.Name);
result = getter(this.Original);
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
field.SetValue(value);
return true;
} else {
var setter = PropertyAccessor.GetSetter(this.Original.GetType(), binder.Name);
setter(this.Original, value);
return true;
}
}
}
而一个衍生类:
public class SelfHydratingPerson : SelfHydratingEntity<IPerson> {
private readonly IDataRepository dataRepository;
public SelfHydratingDerivate(IDataRepository dataRepository, IPerson person)
: base(person) {
this.dataRepository = dataRepository
}
protected override IEnumerable<LoadableBackingField> GetBackingFields() {
yield return new LoadableBackingField("Address",() => this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
这工作完全正常获取和设置属性值,但我得到一个既可以当我隐式转换一个RuntimeBinderException或与一个InvalidCastException明确地将SelfHydratingEntity转换回T.
我知道你可以覆盖DynamicObject.TryConvert方法,但是我想知道究竟是什么这个方法。我今天读了很多关于鸭子打字的文章,并尝试了几个图书馆,但没有一个适用于这种特殊情况。我今天尝试过的所有库都使用Reflection.Emit生成一个包装类,它调用“get_”和“set_”方法,并自然使用反射在包装实例上查找这些方法。 SelfHydratingEntity当然没有定义“get_”和“set_”方法。
所以,我想知道这种事情是否可能。有什么办法将SelfHydratingEntity的实例投射到T?我正在寻找这样的事情:
var original = GetOriginalPerson();
dynamic person = new SelfHydratingPerson(new DataRepository(), original);
string name = person.Name; // Gets property value on original
var address = person.Address; // Gets property value using LoadableBackingField registration
var iPerson = (IPerson)person;
- or -
var iPerson = DuckType.As<IPerson>(person);
你见过这个Duck Typing项目。它看起来不错。我刚刚从Mauricio找到great example。它采用了温莎城堡动态代理拦截方法调用
使用从毛下面的代码工作就像一个梦
class Program
{
static void Main(string[] args)
{
dynamic person = new { Name = "Peter" };
var p = DuckType.As<IPerson>(person);
Console.WriteLine(p.Name);
}
}
public interface IPerson
{
string Name { get; set; }
}
public static class DuckType
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
我对此解决方案进行了一些小的更改: 1.检查IInvocation.Method.Name属性以查看它是属性getter还是setter方法; 2.如果“调用”是属性获取器或设置器,则从动态“目标”字段检索属性; 3.否则,调用IInvocation.Method代表的方法 – 2010-08-16 22:39:16
即兴接口代码 https://github.com/ekonbenefits/impromptu-interface
可以静态浇铸接口上派生的对象来自DynamicObject。
也是一个非常好的答案。不幸的是,一个问题只能有一个答案,否则我会将您的答案标记为“已接受”。非常感谢这篇文章! – 2011-03-04 04:13:57
即兴接口现在位于:https://github.com/ekonbenefits/impromptu-interface – 2015-11-18 20:31:12
当您尝试投射到IPerson时会发生什么? “不起作用”不是很有帮助。 – 2010-07-28 01:06:05
更新后的文章更具体 – 2010-07-28 02:37:39