2011-12-06 67 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