2010-07-28 75 views
2

我寫了一個類,允許派生指定哪些屬性可以延遲加載。該代碼是: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); 
+0

當您嘗試投射到IPerson時會發生什麼? 「不起作用」不是很有幫助。 – 2010-07-28 01:06:05

+0

更新後的文章更具體 – 2010-07-28 02:37:39

回答

2

你見過這個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); 
    } 
} 
+0

我對此解決方案進行了一些小的更改: 1.檢查IInvocation.Method.Name屬性以查看它是屬性getter還是setter方法; 2.如果「調用」是屬性獲取器或設置器,則從動態「目標」字段檢索屬性; 3.否則,調用IInvocation.Method代表的方法 – 2010-08-16 22:39:16

2

即興接口代碼 https://github.com/ekonbenefits/impromptu-interface

可以靜態澆鑄接口上派生的對象來自DynamicObject。

+0

也是一個非常好的答案。不幸的是,一個問題只能有一個答案,否則我會將您的答案標記爲「已接受」。非常感謝這篇文章! – 2011-03-04 04:13:57

+0

即興接口現在位於:https://github.com/ekonbenefits/impromptu-interface – 2015-11-18 20:31:12