C# CodeGenerationHelper – Dynamic Classes – Create Class From Hashtable at Run-time


UPDATE
This post has been depreciated, for an updated version please visit: https://thrash505.wordpress.com/2010/03/18/dynamic-codeclass-generation-update/

Overview
Back a few months ago I had a class that needed (or so I thought) to be generated at run-time. The need arose from WPF controls only working with Properties, not fields, and more specifically ones that implemented INotifyPropertyChanged. (or dependency properties, but I typically save those for custom controls) After some research I created a simple straightforward helper class, CodeGenerationHelper, that would create a dynamic class at run-time from a hashtable. From a hashtable because my solution was working with data received through XML-RPC in the from of an extended hastable implementation. (XmlRpcStruct)


CodeGenerationHelper.cs Listing:

using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.IO;
using Microsoft.CSharp;

namespace CodeGenerationTest
{
    public static class CodeGenerationHelper
    {
        /// <summary>
        /// Creates a dynamic class in memory from a hashtable. Each entry in the hashtable will become a field
        /// with a corresponding property. The class will implement INotifyPropertyChanged for each of the
        /// properties.
        /// </summary>
        /// <param name="namespaceName">The name of the namespace that will contain the dynamic class.</param>
        /// <param name="className">The name of the dynamic class.</param>
        /// <param name="source">The source hashtable that the members of the dynamic class will based off of.</param>
        /// <param name="useKeysForNaming">If set to True, the keys of the hashtable will be converted to strings
        /// and the field names and property names of the dynamic class will be based off of those strings. If
        /// set to false, the fields and properties will be named in sequential order.</param>
        /// <returns>Returns the C# code as a string.</returns>
        public static string CreateClassFromHashtable(string namespaceName, string className, Hashtable source,
            bool useKeysForNaming)
        {
            // Create compile unit.
            CodeCompileUnit compileUnit = new CodeCompileUnit();

            // Create namespace.
            CodeNamespace dynamicNamespace = new CodeNamespace(namespaceName);
            dynamicNamespace.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));

            // Create class.
            CodeTypeDeclaration dynamicClass = new CodeTypeDeclaration(className);
            dynamicClass.IsClass = true;
            dynamicClass.BaseTypes.Add(new CodeTypeReference("System.ComponentModel.INotifyPropertyChanged"));

            // Create PropertyChanged event; implement INotifyPropertyChanged.
            CodeMemberEvent propertyChangedEvent = new CodeMemberEvent();
            propertyChangedEvent.Name = "PropertyChanged";
            propertyChangedEvent.Type = new CodeTypeReference("System.ComponentModel.PropertyChangedEventHandler");
            propertyChangedEvent.Attributes = MemberAttributes.Public;
            dynamicClass.Members.Add(propertyChangedEvent);

            foreach (object key in source.Keys)
            {
                // Construct field and property names.
                string fieldName = string.Format("_{0}", key.ToString());
                string propertyName = key.ToString();

                // Create field.
                CodeMemberField dynamicField = new CodeMemberField(source[key].GetType(), fieldName);
                dynamicField.InitExpression = new CodePrimitiveExpression(source[key]);
                dynamicClass.Members.Add(dynamicField);

                // Create property.
                CodeMemberProperty dynamicProperty = new CodeMemberProperty();
                dynamicProperty.Name = key.ToString();
                dynamicProperty.Type = new CodeTypeReference(source[key].GetType());

                // Create property - get statements.
                dynamicProperty.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));

                // Create property - set statements.
                // Assign value to field.
                dynamicProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));

                // Call PropertyChanged event.
                // Create target object reference.
                CodeEventReferenceExpression propertyChangedTargetObject = new CodeEventReferenceExpression(
                    new CodeThisReferenceExpression(), "PropertyChanged");

                // Create parameters array.
                CodeExpression[] propertyChangedParameters = new CodeExpression[]
                {
                    new CodeThisReferenceExpression(),
                    new CodeObjectCreateExpression("System.ComponentModel.PropertyChangedEventArgs",
                        new CodeExpression[] { new CodePrimitiveExpression(propertyName) })
                };

                // Create delegate invoke expression and add it to the property's set statements; call PropertyChanged.
                CodeDelegateInvokeExpression invokePropertyChanged = new CodeDelegateInvokeExpression(
                    propertyChangedTargetObject, propertyChangedParameters);
                dynamicProperty.SetStatements.Add(invokePropertyChanged);

                // Add property to class.
                dynamicClass.Members.Add(dynamicProperty);
            }

            // Add class to namespace.
            dynamicNamespace.Types.Add(dynamicClass);

            // Add namespace to compile unit.
            compileUnit.Namespaces.Add(dynamicNamespace);

            // Generate CSharp code from compile unit.
            StringWriter stringWriter = new StringWriter();
            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
            provider.GenerateCodeFromCompileUnit(compileUnit, stringWriter, new CodeGeneratorOptions());
            stringWriter.Close();
            return stringWriter.ToString();
        }

        public static object InstantiateClassFromCodeString(string codeString, string fullyQualifiedTypeName)
        {
            CSharpCodeProvider compiler = new CSharpCodeProvider();
            CompilerParameters compilerParams = new CompilerParameters(new string[] { "System.dll" });
            CompilerResults results = compiler.CompileAssemblyFromSource(compilerParams, new string[] { codeString });
            return results.CompiledAssembly.CreateInstance(fullyQualifiedTypeName);
        }
    }
}

Now I’m not going to run through the details of how the class works, but I believe that it is well commented enough that with a little research it is easily understandable. I will demonstrate an example of how the class can be used.

Example Usage:

Hashtable rawCustomer = new Hashtable();
rawCustomer["Id"] = 123456789;
rawCustomer["FirstName"] = "Billy";
rawCustomer["LastName"] = "Bob";
rawCustomer["Rating"] = 84.5D;

string customerCode = CodeGenerationHelper.CreateClassFromHashtable("CodeGenerationTest", "Customer",
    rawCustomer, true);
object customer = CodeGenerationHelper.InstantiateClassFromCodeString(customerCode, "CodeGenerationTest.Customer");

Now there isn’t much to this example simply because that’s as far as I’ve gone with it. I’m sure that it has many possible uses, but I will caution you about going down this route in the first place. A much better solution then generating code at run-time likely exists that will solve your particular problem. For me it was creating a DataTable from the hashtables I received from XML-RPC (for some reason this seems to work at least with the WPF DataGrid, I haven’t tried anything else as of yet). My point is I believe this is going into potentially dangerous areas and you should use this code generation method only as a last resort.

Okay, enough of that and back to the example. First you can see that I create a hashtable and fill it with name/value pairs. Notice though the Rating entry: it has a D suffix at the end of its value to force a type of double. When the code generation is executed this will be respected and enforced as you can see in the output below. Suffixes exist for each of the built-in value types and you can easily find them out with a little bit of research. (although I can’t recall what their formally referred to…) Next I pass the hashtable into the CreateClassFromHashtable method and I get back a complete string representation of our to-be dynamic class. (The output is shown in the section below) Finally we create an instance of our dynamic class. Now this is a very straight forward example and implementation, but I believe the information here provides a good starting point. If I end up continuing down the code generation path I will share my findings, and encourage others to do the same.

Example Output:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.3603
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace DataGridTest {
    using System.ComponentModel;

    public class Customer : System.ComponentModel.INotifyPropertyChanged {

        private double _Rating = 84.5;

        private string _LastName = "Bob";

        private int _Id = 123456789;

        private string _FirstName = "Billy";

        private double Rating {
            get {
                return this._Rating;
            }
            set {
                this._Rating = value;
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Rating"));
            }
        }

        private string LastName {
            get {
                return this._LastName;
            }
            set {
                this._LastName = value;
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("LastName"));
            }
        }

        private int Id {
            get {
                return this._Id;
            }
            set {
                this._Id = value;
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Id"));
            }
        }

        private string FirstName {
            get {
                return this._FirstName;
            }
            set {
                this._FirstName = value;
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("FirstName"));
            }
        }

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    }
}

, , , , ,

  1. #1 by krishna on November 15, 2013 - 7:54 am

    nothing happen in my project and class is not genrated
    please help me

  2. #2 by Atul on March 17, 2010 - 11:43 pm

    All the properties generated are “Private”. Add the following line: dynamicProperty.Attributes = MemberAttributes.Public;

    This will generate the properties as “Public”.

  3. #4 by Anil on March 12, 2010 - 5:49 am

    Hi , In this article class is created , but i disnt access this class , I mean use this class

  1. code de la route

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: