4

DynamicObject的文档中,有一个示例 aDynamicDictionary允许您使用字典,就好像它是具有属性的类一样。

这是课程(为简洁起见稍作修改):

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. 如果是,如何?
4

4 回答 4

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 } };
    
于 2011-12-06T23:52:29.980 回答
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本质上是相同的东西。您也可以查看构建语法的源代码。

于 2011-12-08T13:53:55.440 回答
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()));
}
于 2011-12-22T00:28:04.823 回答
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才能接收 a IDictionary<string, object>,但这只是小菜一碟。

于 2013-07-14T09:43:22.880 回答