2011-10-10 115 views
3

我对我的视图模型使用dynamic对象,因为我发现使用诸如Automapper之类的东西时不必要的开销,并且发现此方法更灵活轻便。我现在用的助洗剂impromptu-interface这样的:将静态类型的对象展开为动态对象

private dynamic New = Builder.New(); 

private dynamic GetViewModel(Product p) 
{ 
    var viewModel = New.Product(id : p.Id, name : p.Name); 
    viewModel.AdditionalProperty = "some additional data"; 
    return viewModel; 
} 

有几种情况中,“扩大”的实际对象将是更好然后重新映射所有属性一个接一个,类似于你将如何使用JavaScript中做jQuery.extend()

private dynamic GetViewModel(Product p) 
{ 
    var viewModel = //create base dynamic object, that has all the members of p. 
    viewModel.AdditionalProperty = "some additional data"; 
    returm viewModel; 
} 

这应该使用ExpandoObject结合反射和通过所有成员迭代可以实现,但想知道是否有一清洁剂/整齐溶液。

+1

我很可能不会仅仅根据性能做出这样的决定,但您确定'动态'比使用Automapper快吗? – svick

+0

这个问题并不是关于性能,而是更轻量,更灵活。一次创建的视图模型不会超过一百个。 –

回答

4

我最终实现这样的:

public class ExpandedObject : DynamicObject 
{ 
    private readonly IDictionary<string, object> expando = new ExpandoObject(); 

    public ExpandedObject(object o) 
    {    
     foreach (var propertyInfo in o.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) 
     { 
      this.expando[propertyInfo.Name] = Impromptu.InvokeGet(o, propertyInfo.Name); 
     } 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    {    
     return this.expando.TryGetValue(binder.Name, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     this.expando[binder.Name] = value; 
     return true; 
    } 
} 

和测试:

[TestFixture] 
public class ExpandedObjectTest 
{ 
    [Test] 
    public void Can_add_new_properties_to_expanded_object() 
    { 
     dynamic expanded = new ExpandedObject(new object()); 
     var data = "some additional data"; 
     expanded.data = data; 
     Assert.AreEqual(data, expanded.data); 
    } 

    [Test] 
    public void Copies_existing_properties() 
    {    
     var obj = new { id = 5 };    
     dynamic expanded = new ExpandedObject(obj);    
     Assert.AreEqual(obj.id, expanded.id);    
    } 
} 

这使得利用Impromptu.InvokeGet(),而不是PropertyInfo.GetValue()因为Impromptu.InvokeGet()使用DLR,因此大约快2.5倍比使用我的测试反射。总的来说,这项工作相当快速,多达10,000个对象的开销几乎不存在。

我应该注意,这不会扩展其他ExpandoObject或类似的,但这不应该真的有必要。

1

您可以创建一个将两个或多个对象的动态对象:

class CombineDynamic : DynamicObject 
{ 
    private readonly object[] m_objects; 

    public CombineDynamic(params object[] objects) 
    { 
     m_objects = objects; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     var callSite = CallSite<Func<CallSite, object, object>>.Create(binder); 

     foreach (var o in m_objects) 
     { 
      try 
      { 
       result = callSite.Target(callSite, o); 
       return true; 
      } 
      catch (RuntimeBinderException) 
      {} 
     } 

     return base.TryGetMember(binder, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     // the binder from argument uses compile time type from call site, 
     // which is object here; because of that, setting of properties that 
     // aren't of type object wouldn't work if we used that binder directly 
     var fixedBinder = Binder.SetMember(
      CSharpBinderFlags.None, binder.Name, typeof(CombineDynamic), 
      new[] 
      { 
       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) 
      }); 

     var callSite = 
      CallSite<Action<CallSite, object, object>>.Create(fixedBinder); 

     foreach (var o in m_objects) 
     { 
      try 
      { 
       callSite.Target(callSite, o, value); 
       return true; 
      } 
      catch (RuntimeBinderException) 
      {} 
     } 

     return base.TrySetMember(binder, value); 
    } 
} 

而且使用这样的:

dynamic viewModel = new CombineDynamic(product, new ExpandoObject()); 
viewModel.AdditionalProperty = "additional data"; 

当你或动态设置属性,它首先试图做在第一个对象上,然后在第二个对象上,直到它成功。

否则像这样已经(至少)一个怪异的行为:如果,例如,Productint物业类型Id,代码viewModel.Id = "42";会成功。但它会在ExpandoObject上设置属性。因此,如果您之后尝试检索viewModel.Id,则会从product.Id返回未修改的int

+0

这更像一个代理工作,但不完全是我想要的。但它应该指向正确的方向:) –

+0

是的,你可以说它是一个代理。你喜欢什么? – svick

+0

我宁愿它做一个浅拷贝到另一个对象。但使用对公共财产的反思应该很容易实现。 –