代码之家  ›  专栏  ›  技术社区  ›  Michael Nero

duck类型动态对象派生

  •  2
  • Michael Nero  · 技术社区  · 14 年前

    我编写了一个类,它允许派生函数指定哪些属性可以延迟加载。代码是:

    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,或者在显式强制转换self-hydrainatingentity返回t时会得到invalidcasteException。

    我知道您可以重写dynamicObject.tryconvert方法,但是我想知道 什么 就是这个方法。我今天读了很多关于duck打字的文章,并尝试了几个库,但没有一个库适用于这个特定的场景。我今天尝试的所有库都使用反射生成了一个包装类。emit调用“get”和“set”方法,并自然地使用反射在包装的实例上找到这些方法。当然,自我保湿没有定义“获取”和“设定”方法。

    所以,我想知道这种事情是否可能。有没有办法给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);
    
    2 回复  |  直到 9 年前
        1
  •  2
  •   Community Tales Farias    7 年前

    你看到这个了吗 Duck Typing 项目。看起来不错。我刚找到一个 great example Mauricio . 它使用windsor castle动态代理拦截方法调用

    使用Mauricio的代码,下面的代码就像一个梦

    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);
        }
    }
    
        2
  •  2
  •   jbtule    9 年前

    即兴接口 https://github.com/ekonbenefits/impromptu-interface

    无法将接口静态转换为从dynamicObject派生的对象。