Dynamically Add C# Properties at Runtime

Dynamically Add C# Properties at Runtime

Have you taken a look at ExpandoObject?

see: https://docs.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject

From MSDN:

The ExpandoObject class enables you to add and delete members of its instances at run time and also to set and get values of these members. This class supports dynamic binding, which enables you to use standard syntax like sampleObject.sampleMember instead of more complex syntax like sampleObject.GetAttribute("sampleMember").

Allowing you to do cool things like:

dynamic dynObject = new ExpandoObject();
dynObject.SomeDynamicProperty = "Hello!";
dynObject.SomeDynamicAction = (msg) =>
{
Console.WriteLine(msg);
};

dynObject.SomeDynamicAction(dynObject.SomeDynamicProperty);

Based on your actual code you may be more interested in:

public static dynamic GetDynamicObject(Dictionary<string, object> properties)
{
return new MyDynObject(properties);
}

public sealed class MyDynObject : DynamicObject
{
private readonly Dictionary<string, object> _properties;

public MyDynObject(Dictionary<string, object> properties)
{
_properties = properties;
}

public override IEnumerable<string> GetDynamicMemberNames()
{
return _properties.Keys;
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (_properties.ContainsKey(binder.Name))
{
result = _properties[binder.Name];
return true;
}
else
{
result = null;
return false;
}
}

public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (_properties.ContainsKey(binder.Name))
{
_properties[binder.Name] = value;
return true;
}
else
{
return false;
}
}
}

That way you just need:

var dyn = GetDynamicObject(new Dictionary<string, object>()
{
{"prop1", 12},
});

Console.WriteLine(dyn.prop1);
dyn.prop1 = 150;

Deriving from DynamicObject allows you to come up with your own strategy for handling these dynamic member requests, beware there be monsters here: the compiler will not be able to verify a lot of your dynamic calls and you won't get intellisense, so just keep that in mind.

Dynamically adding dynamic properties at runtime

You could do something like this:

public static ExpandoObject Extend<T>(this T obj)
{
dynamic eo = new ExpandoObject();
var props = eo as IDictionary<string, object>;

PropertyInfo[] pinfo = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo p in pinfo)
props.Add(p.Name, p.GetValue(obj));

//If you need to add some property known at compile time
//you can do it like this:
eo.SomeExtension = "Some Value";

return eo;
}

This allows you to do this:

var p = new { Prop1 = "value 1", Prop2 = 123 };
dynamic obj = p.Extend();

Console.WriteLine(obj.Prop1); // Value 1
Console.WriteLine(obj.Prop2); // 123
Console.WriteLine(obj.SomeExtension); // Some Value

How to add properties at runtime dynamically to the object c#?


dynamic expando = new ExpandoObject();

// Add properties dynamically to expando
AddProperty(expando, "Language", "English");

public static void AddProperty(ExpandoObject expando, string propertyName, object propertyValue)
{
// ExpandoObject supports IDictionary so we can extend it like this
var expandoDict = expando as IDictionary<string, object>;
if (expandoDict.ContainsKey(propertyName))
expandoDict[propertyName] = propertyValue;
else
expandoDict.Add(propertyName, propertyValue);
}

Thanks to Jay Hilyard and Stephen Teilhet

source: https://www.oreilly.com/content/building-c-objects-dynamically/

Add properties at runtime

You are already creating a collection like this:

PropertyDescriptorCollection propsCollection = 
new PropertyDescriptorCollection(instanceProps.Cast<PropertyDescriptor>().ToArray());

But the collection you are creating only has the existing properties, not your new properties.

You need to supply a single array concatenated from the existing properties and your new properties.

Something like this:

instanceProps.Cast<PropertyDescriptor>().Concat(customProperties).ToArray()

Next problem: you need customProperties which is a collection of PropertyDescriptor. Unfortunately PropertyDescriptor is an abstract class so you don't have an easy way to create one.

We can fix this though, just define your own CustomPropertyDescriptor class by deriving from PropertyDescriptor and implementing all the abstract methods.

Something like this:

public class CustomPropertyDescriptor : PropertyDescriptor
{
private Type propertyType;
private Type componentType;

public CustomPropertyDescriptor(string propertyName, Type propertyType, Type componentType)
: base(propertyName, new Attribute[] { })
{
this.propertyType = propertyType;
this.componentType = componentType;
}

public override bool CanResetValue(object component) { return true; }
public override Type ComponentType { get { return componentType; } }
public override object GetValue(object component) { return 0; /* your code here to get a value */; }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return propertyType; } }
public override void ResetValue(object component) { SetValue(component, null); }
public override void SetValue(object component, object value) { /* your code here to set a value */; }
public override bool ShouldSerializeValue(object component) { return true; }
}

I haven't filled in the calls to get and set your properties; those calls depend on how you've implemented the dynamic properties under the hood.

Then you need to create an array of CustomPropertyDescriptor filled in with information appropriate to your dynamic properties, and concatenate it to the basic properties as I described initially.

The PropertyDescriptor not only describes your properties, it also enables client to actually get and set the values of those properties. And that's the whole point, isn't it!

How do I create dynamic properties in C#?

You might use a dictionary, say

Dictionary<string,object> properties;

I think in most cases where something similar is done, it's done like this.

In any case, you would not gain anything from creating a "real" property with set and get accessors, since it would be created only at run-time and you would not be using it in your code...

Here is an example, showing a possible implementation of filtering and sorting (no error checking):

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1 {

class ObjectWithProperties {
Dictionary<string, object> properties = new Dictionary<string,object>();

public object this[string name] {
get {
if (properties.ContainsKey(name)){
return properties[name];
}
return null;
}
set {
properties[name] = value;
}
}

}

class Comparer<T> : IComparer<ObjectWithProperties> where T : IComparable {

string m_attributeName;

public Comparer(string attributeName){
m_attributeName = attributeName;
}

public int Compare(ObjectWithProperties x, ObjectWithProperties y) {
return ((T)x[m_attributeName]).CompareTo((T)y[m_attributeName]);
}

}

class Program {

static void Main(string[] args) {

// create some objects and fill a list
var obj1 = new ObjectWithProperties();
obj1["test"] = 100;
var obj2 = new ObjectWithProperties();
obj2["test"] = 200;
var obj3 = new ObjectWithProperties();
obj3["test"] = 150;
var objects = new List<ObjectWithProperties>(new ObjectWithProperties[]{ obj1, obj2, obj3 });

// filtering:
Console.WriteLine("Filtering:");
var filtered = from obj in objects
where (int)obj["test"] >= 150
select obj;
foreach (var obj in filtered){
Console.WriteLine(obj["test"]);
}

// sorting:
Console.WriteLine("Sorting:");
Comparer<int> c = new Comparer<int>("test");
objects.Sort(c);
foreach (var obj in objects) {
Console.WriteLine(obj["test"]);
}
}

}
}


Related Topics



Leave a reply



Submit