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 by krishna on November 15, 2013 - 7:54 am
nothing happen in my project and class is not genrated
please help me
#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 by Tim Valentine on March 18, 2010 - 3:27 am
Thanks. I noticed that bug a couple of weeks ago. It seems I posted an older version of the class by mistake. I have created a new post on this topic with updated and additional code. Please visit: https://thrash505.wordpress.com/2010/03/18/dynamic-codeclass-generation-update/
#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
#5 by Tim Valentine on March 18, 2010 - 3:29 am
I have created a new post with examples of how to use the generated class at run-time. Please visit: https://thrash505.wordpress.com/2010/03/18/dynamic-codeclass-generation-update/