C# Duck-Cobject衍生物

C# Duck-Cobject衍生物,c#,.net,dynamic,duck-typing,C#,.net,Dynamic,Duck Typing,我编写了一个类,该类允许派生指定其哪些属性可以延迟加载。代码是: public abstract class SelfHydratingEntity<T> : DynamicObject where T : class { private readonly Dictionary<string, LoadableBackingField> fields; public SelfHydratingEntity(T original) { th

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

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;
        }
    }
}
公共抽象类SelfHydratingEntity:DynamicObject其中T:class{
私有只读字典字段;
公共自主权(T原件){
这个。原创=原创;
this.fields=this.GetBackingFields().ToDictionary(f=>f.Name);
}
公共T原始{get;私有集;}
受保护的虚拟IEnumerable GetBackingFields(){
屈服断裂;
}
公共重写bool TryGetMember(GetMemberBinder绑定器,输出对象结果){
可加载的backingfield字段;
if(this.fields.TryGetValue(binder.Name,out字段)){
结果=field.GetValue();
返回true;
}否则{
var getter=PropertyAccessor.GetGetter(this.Original.GetType(),binder.Name);
结果=getter(这是原始的);
返回true;
}
}
public override bool TrySetMember(SetMemberBinder绑定器,对象值){
可加载的backingfield字段;
if(this.fields.TryGetValue(binder.Name,out字段)){
字段。设置值(值);
返回true;
}否则{
var setter=PropertyAccessor.GetSetter(this.Original.GetType(),binder.Name);
setter(此值为原始值);
返回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));
    }
}
public class SelfHydratingPerson:SelfHydratingEntity{
专用只读IDataRepository数据存储库;
公共自水合衍生物(IDataRepository数据存储库,IPerson个人)
:基数(人){
this.dataRepository=dataRepository
}
受保护的覆盖IEnumerable GetBackingFields(){
返回新的LoadableBackingField(“Address”,()=>this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
这对于获取和设置属性值非常有效,但我在隐式强制转换时会得到RuntimeBinderException,或者会得到一个InvalidCastException,并显式地将SelfIngentity强制转换回T

我知道您可以重写DynamicObject.TryConvert方法,但我想知道在这个方法中到底应该放什么。我今天读了很多关于duck类型的文章,并尝试了几个库,但没有一个适合这种特定场景。我今天尝试的所有库都使用Reflection.Emit生成一个包装器类,该类调用“get_”和“set_”方法,并自然地使用反射在包装实例上查找这些方法。当然,selfhydradingentity没有定义“get_u”和“set_u”方法

所以,我想知道这种事情是否可能发生。有没有什么方法可以向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);
var original=GetOriginalPerson();
动态人员=新的自水合人员(新数据存储库(),原始);
字符串名称=person.name;//获取原始文件的属性值
var address=个人地址;//使用LoadableBackingField注册获取属性值
var iPerson=(iPerson)个人;
-或-
var iPerson=DuckType.As(个人);
你看过这个项目吗。看起来不错。我刚从中找到一份工作。它使用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);
    }
}
类程序
{
静态void Main(字符串[]参数)
{
动态人物=新的{Name=“Peter”};
var p=DuckType.As(个人);
Console.WriteLine(p.Name);
}
}
公共接口IPerson
{
字符串名称{get;set;}
}
公共静态类类型
{
私有静态只读ProxyGenerator=新ProxyGenerator();
公共静态T As(对象o)
{
返回生成器.CreateInterfaceProxyWithoutTarget(新DuckTypingInterceptor(o));
}
}
公共类DuckTypingInterceptor:I Interceptor
{
私有只读对象目标;
公共DuckTypingInterceptor(对象目标)
{
this.target=目标;
}
公共无效拦截(IInvocation调用)
{
var methods=target.GetType().GetMethods()
.Where(m=>m.Name==invocation.Method.Name)
.Where(m=>m.GetParameters().Length==invocation.Arguments.Length)
.ToList();
如果(methods.Count>1)
抛出新的ApplicationException(string.Format(“与{0}'不明确的方法匹配,invocation.method.Name));
if(methods.Count==0)
抛出新的ApplicationException(string.Format(“找不到方法“{0}”),invocation.method.Name));
var方法=方法[0];
if(invocation.GenericArguments!=null&&invocation.GenericArguments.Length>0)
方法=方法.MakeGenericMethod(调用.GenericArguments);
invocation.ReturnValue=method.Invoke(目标、invocation.Arguments);
}
}
即兴界面


静态可以将接口强制转换到从DynamicObject派生的对象上。

当您尝试强制转换到IPerson时会发生什么?“不起作用”不是很有帮助。更新后的帖子更具体一些,我使用了这个解决方案,做了一些小改动:1。检查IInvocation.Method.Name属性,查看它是属性getter还是setter方法;2.如果“调用”是属性getter或setter,则从动态“target”字段检索属性;3.否则,调用IInvocation.method表示的方法也是一个很好的答案。不幸的是,一个问题只能有一个答案,否则我会将你的答案也标记为“已接受”。非常感谢你的帖子!即兴界面现在位于: