Reputation: 23247
I've a class A like that:
public class A
{
private String id; // Generated on server
private DateTime timestamp;
private int trash;
private Humanity.FeedTypeEnum feedType
private List<Property> properties;
// ...
where Property
is:
public class Property
{
private Humanity.PropertyTypeEnum type;
private string key;
private object value;
//...
I'd like to build a dynamic object that flat List<Property> properties
A's field to raw properties. For example:
A a = new A();
a.Id = "Id";
a.Timestamp = DateTime.Now;
a.Trash = 2;
a.FeedType = Humanity.FeedTypeEnum.Mail;
a.Properties = new List<Property>()
{
new Property()
{
Type = Humanity.PropertyTypeEnum.String,
Key = "name"
Value = "file1.pdf"
},
new Property()
{
Type = Humanity.PropertyTypeEnum.Timestamp,
Key = "creationDate",
Value = Datetime.Now
}
}
As I've commented I'd like to flat this a
object in order to access to the properties as:
String name = a.Name;
DateTime creationDate = a.CreationDate;
a.Name = "otherName";
a.CreationDate = creationDate.AddDays(1);
I've achieved that using Reflection. However, I'm figuring out that it's a best option using ExpandoObject
.
The question is, how can I do that using ExpandoObject
class?
Upvotes: 1
Views: 1739
Reputation: 4017
You can do what you want extending DynamicObject
class:
class A : System.Dynamic.DynamicObject
{
// Other members
public List<Property> properties;
private readonly Dictionary<string, object> _membersDict = new Dictionary<string, object>();
public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
{
result = null;
if (!_membersDict.ContainsKey(binder.Name))
return false;
result = _membersDict[binder.Name];
return true;
}
public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
{
if (!_membersDict.ContainsKey(binder.Name))
return false;
_membersDict[binder.Name] = value;
return true;
}
public void CreateProperties()
{
foreach (Property prop in properties)
{
if (!_membersDict.ContainsKey(prop.key))
{
_membersDict.Add(prop.key, prop.value);
}
}
}
}
Then use it like that:
A a = new A();
/////////
a.Properties = new List<Property>()
{
new Property()
{
Type = Humanity.PropertyTypeEnum.String, // Now this property is useless, the value field has already a type, if you want you can add some logic around with this property to ensure that value will always be the same type
Key = "name"
Value = "file1.pdf"
},
new Property()
{
Type = Humanity.PropertyTypeEnum.Timestamp,
Key = "creationDate",
Value = Datetime.Now
}
}
a.CreateProperties();
dynamic dynA = a;
dynA.name = "value1";
Upvotes: 2
Reputation: 430
This method just overrides the square bracket operator. Not as slick as what you wanted but simpler and workable. You definitely can't have design time (when you write code) features like you described. You could make it work with reflection at run-time but surely not the way you showed right?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
A a = new A();
a.properties = new List<Property>()
{
new Property(Humanity.PropertyTypeEnum.String, "name", "file1.pdf"),
new Property(Humanity.PropertyTypeEnum.Timestamp, "creationDate", DateTime.Now)
};
//String name = a.Name;
//DateTime creationDate = a.CreationDate;
//a.Name = "otherName";
//a.CreationDate = creationDate.AddDays(1);
String name = (String)a["name"];
DateTime creationDate = (DateTime)a["creationDate"];
a["name"] = "otherName";
a["creationDate"] = creationDate.AddDays(1);
}
public class A
{
private Dictionary<Property, object> myvalues = new Dictionary<Property, object>();
public List<Property> properties;
public object this[string name]
{
get
{
var prop = properties.Find(p => p.key == name);
if (prop == null)
throw new Exception("property " + name + " not found.");
return prop.value;
}
set
{
var prop = properties.Find(p => p.key == name);
if (prop == null)
throw new Exception("property " + name + " not found.");
prop.value = value;
}
}
}
public class Property
{
public Property(Humanity.PropertyTypeEnum type, string key, object value) { this.type = type; this.key = key; this.value = value; }
private Humanity.PropertyTypeEnum type;
public string key;
public object value;
}
}
}
namespace Humanity
{
public enum PropertyTypeEnum { String, Timestamp };
public enum FeedTypeEnum { };
}
Upvotes: 1