2011-12-06 42 views
3

DynamicObject的文档中,有一个DynamicDictionary的示例,它允许您使用字典,就好像它是具有属性的类。如何编写支持对象初始化程序的自定义DynamicObject类

这里是类(为简便起见稍作修改):

public class DynamicDictionary : DynamicObject 
{ 
    Dictionary<string, object> _dictionary = new Dictionary<string, object>(); 

    public int Count 
    { 
     get { return _dictionary.Count; } 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     string name = binder.Name.ToLower(); 
     return _dictionary.TryGetValue(name, out result); 
    } 

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

我希望做的是修改类,这样我可以做到以下几点:

public class Test 
{ 
    public Test() 
    { 
     var result = Enumerable.Range(1, 5).Select(i => new DynamicDictionary 
     { 
      Id = i, 
      Foo = "Foo", 
      Bar = 2 
     }); 
    } 
} 

问题

  1. 这可能吗?
  2. 如果是,如何?
+0

对象初始化程序不适用于动态对象 – Magnus

+0

@Magnus在技术上可以与动态对象一起使用,但只能以非动态方式使用。所以如果你的动态对象具有非动态的'Id'属性,你可以使用它的对象初始值设定项。但这无助于回答这个问题。 – svick

+0

确实,对于“正常”属性,ti'll可以正常工作。我猜你colud添加一个构造函数,它接受'IEnumerable >'并使用它来创建Dictionary。 – Magnus

回答

0

事实证明,我能够通过使用Linq的内置ToDictionary()方法来解决我的问题。

例子:

public Test() 
{ 
    var data = Enumerable.Range(1, 5).Select(i => new 
    { 
     Id = i, 
     Foo = "Foo", 
     Bar = 2 
    }); 
    var result = data 
     .Select(d => d.GetType().GetProperties() 
      .Select(p => new { Name = p.Name, Value = p.GetValue(pd, null) }) 
      .ToDictionary(
       pair => pair.Name, 
       pair => pair.Value == null ? string.Empty : pair.Value.ToString())); 
} 
4

DynamicObject提供了TryCreateInstance(),这是针对这样的情况,但它不适用于C#。

我看到一些方法解决此问题:

  1. 创建一个动态的工厂类。当你调用它的Create()法命名变元的,将其传递到字典:

    class DynamicDictionaryFactory : DynamicObject 
    { 
        public override bool TryInvokeMember(
         InvokeMemberBinder binder, object[] args, out object result) 
        { 
         if (binder.Name == "Create") 
         { 
          // use binder.CallInfo.ArgumentNames and args 
          // to create the dynamic dictionary 
          result = …; 
          return true; 
         } 
    
         return base.TryInvokeMember(binder, args, out result); 
        } 
    } 
    
    … 
    
    dynamic factory = new DynamicDictionaryFactory(); 
    
    dynamic dict = factory.Create(Id: 42); 
    
  2. 使用非动态集合初始化。这意味着具有属性名称作为代码字符串:

    // has to implement IEnumerable, so that collection initializer works 
    class DynamicDictionary 
        : DynamicObject, IEnumerable<KeyValuePair<string, object>> 
    { 
        public void Add(string name, object value) 
        { 
         m_dictionary.Add(name, value); 
        } 
    
        // IEnumerable implmentation and actual DynamicDictionary code here 
    } 
    
    … 
    
    dynamic dict = new DynamicDictionary { { "Id", 42 } }; 
    
  3. 可能是最接近你问什么是使用嵌套对象初始化。也就是说,类将有一个dynamic属性(比如,Values),其属性可以使用对象初始化设置:

    class DynamicDictionary : DynamicObject 
    { 
        private readonly IDictionary<string, object> m_expandoObject = 
         new ExpandoObject(); 
    
        public dynamic Values 
        { 
         get { return m_expandoObject; } 
        } 
    
        // DynamicDictionary implementation that uses m_expandoObject here 
    } 
    
    … 
    
    dynamic dict = new DynamicDictionary { Values = { Id = 42 } }; 
    
1

使用开源ImpromptuInterface(通过的NuGet)它有一个Builder Syntax。这可以让你做一些接近初始化语法的东西。包括ImpromptuInterface.Dynamic后具体来说,你可以做

var result = Enumerable.Range(1, 5).Select(i => Build<DynamicDictionary>.NewObject 
    (
     Id: i, 
     Foo: "Foo", 
     Bar: 2 
    )); 

有一些语法页面上也列出即使你删除掉<DynamicDictionary>还会使用一个ImpromptuDictionary基本上是同样的事情其他选项。你也可以查看构建语法的source

0

当我看到这样的嵌入式语言的方式,我倾向于使用扩展方法,这样我就可以写下面的代码来实现我的目标:

public Test() 
{ 
    var data = Enumerable.Range(1, 5).Select(i => new 
    { 
     Id = i, 
     Foo = "Foo", 
     Bar = 2 
    }.AsDynamicDictionary()); 
} 

然后,我将定义扩展方法的代码将任何object(包括匿名类型的)转换为DynamicDictionary

public static class DynamicDictionaryExtensions 
{ 
    public static DynamicDictionary AsDynamicDictionary(this object data) 
    { 
     if (data == null) throw new ArgumentNullException("data"); 
     return new DynamicDictionary(
       data.GetType().GetProperties() 
       .Where(p => p. && p.CanRead) 
       .Select(p => new {Name: p.Name, Value: p.GetValue(data, null)}) 
       .ToDictionary(p => p.Name, p => p.Value) 
     ); 
    } 
} 

您将不得不在DynamicDictionary中实现构造函数以获得IDictionary<string, object>,但这是小菜一碟。

+0

'p => p。 && p.CanRead'你似乎在这里错过了一些东西。 – svick