Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dynamic Properties Implementation Using C# Generics

0.00/5 (No votes)
30 Oct 2007 1  
Dynamic properties implementation using C# generics.

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:

/// <summary />
/// Abstract class for all dynamic properties.
/// </summary />
public abstract class DynamicProperty<T>
{
  // public methods...
  /// <summary />
  /// Gets value of child property with the given name.
  /// </summary />
  /// <typeparam name="TValue" />The value type of dynamic property.</typeparam />
  /// <param name="name">The name of the property to get.</param>
  public abstract TValue GetValue<TValue>(string name);

  /// <summary />
  /// Sets value of child property with the given name.
  /// </summary />
  /// <typeparam name="TValue" />The value type of dynamic property.</typeparam />
  /// <param name="name">The name of the property to get.</param>
  /// <param name="value">The value to set.</param>
  public abstract void SetValue<TValue>(string name, TValue value);

  /// <summary />
  /// Returns true if there is a child property
  /// with the given name inside this dynamic property.
  /// </summary />
  /// <param name="name">The name of the property to check.</param>
  public abstract bool HasProperty(string name);

  /// <summary />
  /// Adds child property to this dynamic property.
  /// </summary />
  /// <typeparam name="TValue" />The value type of dynamic property.</typeparam />
  /// <param name="property">The property to add.</param>
  public abstract void AddProperty<TValue>(string name,
         DynamicProperty<TValue> property);

  /// <summary />
  /// Removes child property from this dynamic property.
  /// </summary />
  /// <param name="name">The name of the property to remove.</param>
  public abstract void RemoveProperty(string name);

  /// <summary />
  /// Gets child dynamic property with the given name.
  /// </summary />
  /// <typeparam name="TValue" />The value type of dynamic property.</typeparam />
  /// <param name="name">The name of the child property to get.</param>
  public abstract DynamicProperty<TValue> GetProperty<TValue>(string name);

  // public properties...
  /// <summary />
  /// Gets or sets value of this property.
  /// </summary />
  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:

/// <summary />
/// Simple dynamic property.
/// </summary />
public class SimpleProperty<T> : DynamicProperty<T>
{
  T _value;

  // constructors...
  public SimpleProperty(T value)
  {
    _value = value;
  }

  // public methods...
  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 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:

// String property:
DynamicProperty<string> stringProperty = 
new SimpleProperty<string>("StringValue");
stringProperty.Value = "Hello";
Console.WriteLine("String property value: {0}", stringProperty.Value);

// Boolean property:
DynamicProperty<bool> boolProperty = new SimpleProperty<bool>(false);
boolProperty.Value = true;
Console.WriteLine("Boolean property value: {0}", 
boolProperty.Value);

// Integer property:
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:

/// <summary>
/// Complex dynamic property implementation.
/// </summary>
public class ComplexProperty<T> : DynamicProperty<T>
{
  Dictionary<string, object> _properties;

  // constructors...
  public ComplexProperty()
  {
  }

  // private properties...
  Dictionary<string, object> Properties
  {
    get
    {
      if (_properties == null)
        _properties = new Dictionary<string, object>();
      return _properties;
    }
  }

  // public methods...
  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 properties...
  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:

// Create complex dynamic property and add child properties:
DynamicProperty<object> complexProperty = new ComplexProperty<object>();
complexProperty.AddProperty("StringProperty", stringProperty);
complexProperty.AddProperty("BooleanProperty", boolProperty);
complexProperty.AddProperty("IntegerProperty", intProperty);

// We can get or set value of child properties now:
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:

/// <summary>
/// Customer properties.
/// </summary>
public class CustomerDetails : ComplexProperty<object>
{
  // constructors...
  public CustomerDetails()
  {
    AddProperty("FirstName", new SimpleProperty<string>(String.Empty));
    AddProperty("LastName", new SimpleProperty<string>(String.Empty));
  }

  // public properties...
  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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here