This question already has answers here:
can one convert a dynamic object to an ExpandoObject (c#)
(2 answers)
How to extend an existing object in c# 4.0 using dynamics
(1 answer)
Closed 2 years ago.
I've read a lot about how ExpandoObject can be used to dynamically create objects from scratch by adding properties, but I haven't yet found how you do the same thing starting from a non-dynamic C# object that you already have.
For instance, I have this trivial class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Telephone { get; set; }
}
I would like to convert this to ExpandoObject so that I can add or remove properties based on what it has already, rather than rebuilding the same thing from scratch. Is this possible?
Edit: the questions marked as duplicate are clearly NOT duplicates of this one.
It could be done like this:
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));
You can not "convert" a Person
class in to a expando object. However you could create a wrapper DynamicObject
that contains a Person
and forwards all of the fields.
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;
}
}
}
example usage
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; }
}
}