Introduction
Some programming tasks require dynamic nature of properties exposed by an object. E.g., it might be needed to access an object property by a given key, it might be needed to get all the object properties and iterate over them. Dynamic properties are useful when you need to manage them at runtime, when your object is already instantiated. In this article, we are going to create a simple implementation of dynamic properties using the C# programming language. We'll use generics for our dynamic properties to make the implementation more flexible and to avoid boxing operations when value types are used for the underlying property values.
Using the Code
DynamicProperty Abstract Class
First of all, let's define what we need from a dynamic property. Generally speaking, a dynamic property should allow us to get or set a value associated with it if any. A dynamic property may also be complex and may contain child dynamic properties, thus forming a complex property tree, so we will also need methods to add or remove child dynamic properties, and a method to check if a child property exists. For most tasks, it's OK to reference a dynamic property by its string name, so we will use a string parameter to reference the child dynamic properties.
Here is the list of the basic functionality we'll need:
- Get or set the value of the dynamic property.
- Get or set the value of the child property using a string name.
- Add or remove a child property.
- Check if the child property exists.
This is a general set of operations we will create inside our dynamic property abstract class. In this article, we will not cover the methods to access a collection of all child dynamic properties; however, it doesn't look too hard to implement this, so we'll leave this implementation as an exercise.
Here is the C# code for the DynamicProperty
abstract class:
public abstract class DynamicProperty<T>
{
public abstract TValue GetValue<TValue>(string name);
public abstract void SetValue<TValue>(string name, TValue value);
public abstract bool HasProperty(string name);
public abstract void AddProperty<TValue>(string name,
DynamicProperty<TValue> property);
public abstract void RemoveProperty(string name);
public abstract DynamicProperty<TValue> GetProperty<TValue>(string name);
public abstract T Value
{
get;
set;
}
}
Note that we are using the generic abstract class with the T
generic type parameter for the property value type. This type parameter is only used as a return type of the Value
abstract property, and there is no field with the T
type inside the DynamicProperty
class, so the T
type parameter will be needed for the descendants. The DynamicProperty
class defines several abstract methods for adding or removing child dynamic properties. The GetValue
and SetValue
methods can be used to access a value of a child property with the specified name. All methods for child property manipulation have a TValue
generic type parameter (except for the HasProperty
method, since it only needs a child property name).
SimplePropery Class
The DynamicProperty
class is not useful by itself as it contains only abstract methods, we can do nothing yet. So we need to define a simple DynamicProperty
descendant to represent a leaf dynamic property: SimpleProperty
. SimpleProperty
doesn't have any child properties and doesn't allow adding or removing them, instead it only has a value.
Here is the C# code for the SimpleProperty
class:
public class SimpleProperty<T> : DynamicProperty<T>
{
T _value;
public SimpleProperty(T value)
{
_value = value;
}
public override TValue GetValue<TValue>(string name)
{
throw new InvalidOperationException("Can't get property value.");
}
public override void SetValue<TValue>(string name, TValue value)
{
throw new InvalidOperationException("Can't set property value.");
}
public override bool HasProperty(string name)
{
return false;
}
public override void AddProperty<TValue>(string name,
DynamicProperty<TValue> property)
{
throw new InvalidOperationException("Can't add child properties.");
}
public override void RemoveProperty(string name)
{
throw new InvalidOperationException("Can't remove child properties.");
}
public override DynamicProperty<TValue> GetProperty<TValue>(string name)
{
throw new InvalidOperationException("Can't get child properties.");
}
public override T Value
{
get { return _value; }
set { _value = value; }
}
}
Now we have the ability to create a simple dynamic property and assign a value to it. Let's try to create simple properties with string, boolean, and integer value types. Here is the sample code:
DynamicProperty<string> stringProperty =
new SimpleProperty<string>("StringValue");
stringProperty.Value = "Hello";
Console.WriteLine("String property value: {0}", stringProperty.Value);
DynamicProperty<bool> boolProperty = new SimpleProperty<bool>(false);
boolProperty.Value = true;
Console.WriteLine("Boolean property value: {0}",
boolProperty.Value);
DynamicProperty<int> intProperty = new SimpleProperty<int>(20);
intProperty.Value = 100;
Console.WriteLine("Integer property value: {0}", intProperty.Value);
The sample above is pretty clear: each property has its own type and value passed inside the simple property constructor. The value is used to get or set the associated property value. A simple property is a basic implementation of a dynamic property, and it is not very useful alone. Again, we are going to create another descendant of the DynamicProperty
abstract class to support child dynamic properties.
ComplexProperty Class
The most interesting part of this article starts here as the complex dynamic property class contains the core implementation of our dynamic properties concept. I should mention that we used the same concept to represent dynamic properties of the text element and formatting objects inside our product for text document text manipulation. The complex property class overrides the abstract methods of its ancestor class and adds implementation for supporting child dynamic properties. We will use the System.Collections.Generic.Dictionary<string,>
class to store the child properties.
Here is the C# code for the ComplexProperty
class:
public class ComplexProperty<T> : DynamicProperty<T>
{
Dictionary<string, object> _properties;
public ComplexProperty()
{
}
Dictionary<string, object> Properties
{
get
{
if (_properties == null)
_properties = new Dictionary<string, object>();
return _properties;
}
}
public override TValue GetValue<TValue>(string name)
{
DynamicProperty<TValue> property = GetProperty<TValue>(name);
if (property != null)
return property.Value;
return default(TValue);
}
public override void SetValue<TValue>(string name, TValue value)
{
DynamicProperty<TValue> property = GetProperty<TValue>(name);
if (property != null)
property.Value = value;
}
public override bool HasProperty(string name)
{
return _properties == null ? false : Properties.ContainsKey(name);
}
public override void AddProperty<TValue>(string name,
DynamicProperty<TValue> property)
{
if (HasProperty(name))
throw new InvalidOperationException("Can't add property.");
Properties.Add(name, property);
}
public override void RemoveProperty(string name)
{
Properties.Remove(name);
}
public override DynamicProperty<TValue> GetProperty<TValue>(string name)
{
if (!HasProperty(name))
throw new InvalidOperationException("Can't get property.");
DynamicProperty<TValue> property = Properties[name] as DynamicProperty<TValue>;
if (property == null)
throw new InvalidOperationException("Invalid type specified.");
return property;
}
public override T Value
{
get { return default(T); }
set { }
}
}
Note that ComplexProperty
doesn't have any value associated with it, so when creating a complex property, we will use the object
type as the generic type argument (it is also possible to declare the ComplexPoperty
class without generic type parameters, we'll leave this as an exercise). Inside the GetValue
and SetValue
methods, we call the GetProperty
method first and then get or set the underlying property value. We can use complex properties together with simple properties to build complex dynamic property trees. Let's try to create a complex dynamic property with three simple child properties from the example above:
DynamicProperty<object> complexProperty = new ComplexProperty<object>();
complexProperty.AddProperty("StringProperty", stringProperty);
complexProperty.AddProperty("BooleanProperty", boolProperty);
complexProperty.AddProperty("IntegerProperty", intProperty);
complexProperty.SetValue<string>("StringProperty",
"Child String Value");
Console.WriteLine("Complex property child string: {0}",
complexProperty.GetValue<string>("StringProperty"));
complexProperty.SetValue<bool>("BooleanProperty", false);
Console.WriteLine("Complex property child booean: {0}",
complexProperty.GetValue<bool>("BooleanProperty"));
complexProperty.SetValue<int>("IntegerProperty", 200);
Console.WriteLine("Complex property child integer: {0}",
complexProperty.GetValue<int>("IntegerProperty"));
In the example above, we create an instance of the ComplexProperty
class and then add the dynamic child properties. Then, we use the SetValue
and GetValue
methods to easily access the child property values. Note that the AddProperty
method calls do not have type arguments specified. This is possible because the C# compiler uses type inference to determine which type to use for the TValue
generic type parameter.
Exposing Dynamic Properties
Simple and complex dynamic properties can be used to dynamically create property trees using the AddProperty
and RemoveProperty
methods. However, we found that it is a good idea to expose dynamic properties as fixed
via class properties when it is known that properties won't change at runtime. E.g., for a customer class, we can say for sure that the FirstName
and LastName
properties won't be ever removed from the customer data type, so it makes sense to expose these dynamic properties. Here is the C# code example:
public class CustomerDetails : ComplexProperty<object>
{
public CustomerDetails()
{
AddProperty("FirstName", new SimpleProperty<string>(String.Empty));
AddProperty("LastName", new SimpleProperty<string>(String.Empty));
}
public string FirstName
{
get { return GetValue<string>("FirstName"); }
set { SetValue<string>("FirstName", value); }
}
public string LastName
{
get { return GetValue<string>("LastName"); }
set { SetValue<string>("LastName", value); }
}
}
In the example above, we expose the FirstName
and LastName
string properties, but we call the GetValue
and SetValue
methods inside the getter and setter of these properties. This makes it easy to work with the CustomerDetails
class without even knowing that FirstName
and LastName
are dynamic properties.
Conclusion
In this article, we implemented the simple dynamic properties concept using C# generic classes. Dynamic properties is a very flexible concept, and can be used when you need to have a class with a dynamic nature.
Points of Interest
There is still functionality left behind the scope of this article, e.g., Typed Relationship, Dynamic Property Knowledge Level, and Extrinsic concepts. For related reading, I recommend the "Dealing with Properties" (PDF) article by Martin Fowler.
History
- October 31, 2007: Initial release.