【问题标题】:How do you convert any C# object to an ExpandoObject? [duplicate]如何将任何 C# 对象转换为 ExpandoObject? [复制]
【发布时间】:2018-04-07 11:31:22
【问题描述】:

我已经阅读了很多关于如何使用 ExpandoObject 通过添加属性从头开始动态创建对象,但我还没有找到您如何从非您已经拥有的动态 C# 对象。

例如,我有这个微不足道的类:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Telephone { get; set; }
}

我想将其转换为 ExpandoObject,以便我可以根据已有的属性添加或删除属性,而不是从头开始重建相同的东西。这可能吗?

编辑:标记为重复的问题显然不是这个问题的重复。

【问题讨论】:

  • 也许您可以创建一个 Dictionary 包含来自 Person 类的所有属性并将其转换为动态对象? stackoverflow.com/questions/15819720/…
  • @PanagiotisKanavos dynamic “不会神奇地使您的对象可扩展”stackoverflow.com/a/36558165
  • 这不实用。如果 Person 只有公共字段,你可以到达某个地方,但属性是不行的。您无法通过 ExpandoObject 调用 getter 和 setter。它实际上只是一个与动态绑定器一起工作的类,动态绑定器是 C# 编译器的一部分,它有助于将 eo["foo"] = 42 之类的语句转换为 DLR 调用。在 Javascript 或 Python 等动态语言中常见的那种语句。从除了构造函数之外没有公共成员的 ExpandoObject 也可以看出。
  • @PanagiotisKanavos 恕我直言,请不要给我这个“太宽泛”的垃圾。我的问题再清楚不过了,可以非常具体地回答。如果您对自己所说的话有信心,则可以发布答案。但是没有理由对提出的问题感到迂腐,因为这已成为 Stack Overflow 上的一个大问题。
  • 正确,其他的不是重复的。我的搜索结果首先建议了这个。所以欢迎来到 SO 的巨大缺点,它是不合理的反对票。

标签: c# dynamic expandoobject


【解决方案1】:

可以这样做:

var person = new Person { Id = 1, Name = "John Doe" };

var expando = new ExpandoObject();
var dictionary = (IDictionary<string, object>)expando;

foreach (var property in person.GetType().GetProperties())
    dictionary.Add(property.Name, property.GetValue(person));

【讨论】:

  • 我想澄清一下这个解决方案,这不是让 Person 成为一个 expando 对象,而是将 Person 中的属性复制到一个新的 expando 对象中。如果你这样做expando.Id = 2person.Id 的值仍然是 1
  • @斯科特张伯伦。是的。你是对的。
  • 我的问题确实询问了如何根据现有对象的值创建 ExpandoObject,所以我认为这很好。
  • 解释:如果你查看 ExpandoObject,你可以看到它实现了IDictionary&lt;string, object&gt;。如果你在运行时观察对象,你可以看到一个Results View,它是那个字典的枚举。所以这里发生的事情是:您创建一个空的ExpandoObject,然后获取该对象的一个​​向上转换的实例来手动操作其 IDictionary Form。 expando 对象正在被直接修改,即使看起来您只是在修改字典实例。最后,您已手动将属性添加到 expando 实例。
【解决方案2】:

您不能将 Person 类“转换”为 expando 对象。但是,您可以创建一个包装器 DynamicObject,其中包含一个 Person 并转发所有字段。

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;

namespace SandboxConsole
{
    public class ExpandoWrapper : DynamicObject
    {
        private readonly object _item;
        private readonly Dictionary<string, PropertyInfo> _lookup = new Dictionary<string, PropertyInfo>(StringComparer.InvariantCulture);
        private readonly Dictionary<string, PropertyInfo> _ignoreCaseLookup = new Dictionary<string, PropertyInfo>(StringComparer.InvariantCultureIgnoreCase);

        private readonly Dictionary<string, Box> _lookupExtra = new Dictionary<string, Box>(StringComparer.InvariantCulture);
        private readonly Dictionary<string, Box> _ignoreCaseLookupExtra = new Dictionary<string, Box>(StringComparer.InvariantCultureIgnoreCase);

        private class Box
        {
            public Box(object item)
            {
                Item = item;
            }
            public object Item { get; }
        }

        public ExpandoWrapper(object item)
        {
            _item = item;
            var itemType = item.GetType();
            foreach (var propertyInfo in itemType.GetProperties())
            {
                _lookup.Add(propertyInfo.Name, propertyInfo);
                _ignoreCaseLookup.Add(propertyInfo.Name, propertyInfo);
            }
        }
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;
            PropertyInfo lookup;
            if (binder.IgnoreCase)
            {
                _ignoreCaseLookup.TryGetValue(binder.Name, out lookup);
            }
            else
            {
                _lookup.TryGetValue(binder.Name, out lookup);
            }

            if (lookup != null)
            {
                result = lookup.GetValue(_item);
                return true;
            }

            Box box;
            if (binder.IgnoreCase)
            {
                _ignoreCaseLookupExtra.TryGetValue(binder.Name, out box);
            }
            else
            {
                _lookupExtra.TryGetValue(binder.Name, out box);
            }

            if (box != null)
            {
                result = box.Item;
                return true;
            }

            return false;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            PropertyInfo lookup;
            if (binder.IgnoreCase)
            {
                _ignoreCaseLookup.TryGetValue(binder.Name, out lookup);
            }
            else
            {
                _lookup.TryGetValue(binder.Name, out lookup);
            }

            if (lookup != null)
            {
                lookup.SetValue(_item, value);
                return true;
            }

            var box = new Box(value);
            _ignoreCaseLookupExtra[binder.Name] = box;
            _lookupExtra[binder.Name] = box;

            return true;
        }
    }
}

示例用法:

using System;

namespace SandboxConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person() {Id = 1};
            dynamic wrapper = new ExpandoWrapper(person);

            wrapper.Id = 2;
            wrapper.NewField = "Foo";

            Console.WriteLine(wrapper.Id);
            Console.WriteLine(person.Id);
            Console.WriteLine(wrapper.NewField);
        }
    }
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string Telephone { get; set; }
    }
}

【讨论】:

  • 我在想这样的事情:)
  • JsonConvert.SerializeObject(wrapper) 在此示例中变为 {}
  • @Suamere 好吧,这是一种黑客解决方法,我对错误发生并不感到惊讶。
  • @ScottChamberlain 如何解决序列化问题?
猜你喜欢
  • 2020-06-16
  • 2016-08-02
  • 2022-01-24
  • 2015-12-13
  • 2021-09-08
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 2020-05-02
相关资源
最近更新 更多