Ross Buggins

Partner at Shore Tech Systems LLP a Norwich based IT consultancy

The fastest way to dynamically create C# objects

by Ross Buggins1. February 2011 18:39
In a current project I am dynamically creating an object of a certain type depending on a value read back from the database. For example, in the data reader results i may have 2 results coming back, the first with a object type value of ‘STS.Person’ and the second with a type of ‘STS.Location’. The result with a object type of ‘Sts.Person’ should have a C#  type created Person() and the result with an object type of ‘Sts.Location’ should create C# type Location().  Person() and Location() both implement the interface ISTSObjects() so we can return  List<ISTSObjects>(). The quickest way to create the correct object is to do a case statement in the read section. 1 public IStsObject LoadCorrectObject(string objectType) 2 { 3 switch (objectType) 4 { 5 case "Sts.Person": 6 { 7 return new Person(); 8 } 9 case "Sts.Location": 10 { 11 return new Location(); 12 } 13 default: 14 { 15 return null; 16 17 } 18 } 19 } (All the source code for this blog is at http://www.shoretechsystems.co.uk/blogrb/file.axd?file=2011%2f2%2fProgram.cs) However, this isn’t very flexible. What we need to do is to be able to dynamically create the correct instance based on the string input.  This requires keeping a record of how to make each object in the database, at a minimum this means keeping a class (fully qualified including namespaces) and assembly name for each type we want to be able to dynamically create. 1 public IStsObject LoadCorrectObject(string objectType) 2 { 3 var className = LoadClassNameFromDataBase(objectType); 4 var assemblyName = LoadClassNameFromDataBase(objectType); 5 6 var objHandle = System.Activator.CreateInstance(assemblyName, className); 7 var stsObject = objHandle.Unwrap() as IStsObject; 8 return stsObject; 9 } This uses the Activator class to dynamically create an instance of a class from the string value of its assembly and class name. However, the problem with this is that if you return a 1000 objects from the database you will be calling CreateInstance a thousand time which when passing string parameters is very slow. So, you need to implement some sort of caching. It becomes more clear what to do once you see that CreateInstance is over ridden and can take Type objects and create instance of them. What we then do is only use our string values once.  We get our object, call .GetType() on it and cache this in memory along with our string key. This means the next time we need an instance of it we can pass the Type object to the Activator to create an instance of it. 1 public IStsObject LoadCorrectObject(string objectType) 2 { 3 string className = LoadClassNameFromDataBase(objectType); 4 string assemblyName = LoadAssemblyNameFromDataBase(objectType); 5 6 if(!dic.ContainsKey(objectType)) 7 { 8 var objHandle = System.Activator.CreateInstance(assemblyName, className); 9 var stsObject = objHandle.Unwrap() as IStsObject; 10 dic.Add(objectType, stsObject); 11 } 12 13 return dic[objectType]; 14 } Now CreateInstance is working with the Type object not the string values. This is originally where I had stopped, but I was thinking that the must be a way to create dynamic objects without using the Activator at all. And then it dawned on my to use a loader class for each actual class and methods which return Func<IStsObject>. For each class I will dynamically create I also create a loader class which implements the ILoader interface.  This interface has a single method declaration of type Func<IStsObject>.  Each class that implements the interface simply has to return a new object of itself in the function. We then only store this function in the dictionary, not a type object.  When we need it, we retrieve the correct function and just invoke it. The loader interface: 1 public interface ILoader 2 { 3 Func<IStsObject> Load(); 4 } 5 6 public class PersonLoader : ILoader 7 { 8 public Func<IStsObject> Load() 9 { 10 return () => new Person(); 11 } 12 } The code to store and use the functions:   1 Dictionary<string, Func<IStsObject>> funcDic = new Dictionary<string, Func<IStsObject>>(); 2 3 public IStsObject LoadCorrectObject(string objectType) 4 { 5 string loaderClassName = LoadLoaderClassNameFromDataBase(objectType); 6 string assemblyName = LoadAssemblyNameFromDataBase(objectType); 7 8 if(!funcDic.ContainsKey(objectType)) 9 { 10 var objHandle = System.Activator.CreateInstance(assemblyName, loaderClassName); 11 var stsObject = objHandle.Unwrap() as ILoader; 12 var loaderFunction = stsObject.Load(); 13 funcDic.Add(objectType, loaderFunction); 14 } 15 16 return funcDic[objectType].Invoke(); 17 } When invoke is called on a type of STS.Person what is actually called is new Person() The Activator is only used once per type, and that is to load my loader class, not the actual class we are dynamically loading! I have created a test bed to measure the time differences between these options. The four test cases are: Control – This creates a class directly from the new keyword, there is nothing dynamic, and is only here for comparisons – this is the quickest way (as per my original case statement). Test 1 – Calling CreateInstance everytime with string class and assembly name parameters. Test 2 – Calling CreateInstance on the type object after loading it from the string class and assembly names only once then caching the type object. Test 3 – Using loader classes with func methods and Invoking. The Test Case code is : 1 public static class TestCases 2 { 3 public static Func<IStsObject> ControlTest(string n) 4 { 5 return () => new Person(); 6 } 7 8 public static Func<IStsObject> Test1(string n) 9 { 10 return () => 11 { 12 var x = DB.Read(n); 13 var a = System.Activator.CreateInstance(x.AssemblyName, x.ClassName); 14 var b = a.Unwrap() as IStsObject; 15 return (IStsObject)b; 16 }; 17 } 18 19 public static Func<IStsObject> Test2(string n) 20 { 21 return () => TypeLoader.GetObject(n); 22 } 23 24 public static Func<IStsObject> Test3(string n) 25 { 26 return () => FuncLoader.GetObject(n); 27 } 28 }   With supporting code of: 1 2 3 /// <summary> 4 /// my object interface 5 /// </summary> 6 public interface IStsObject 7 { 8 9 } 10 11 /// <summary> 12 /// my person class 13 /// </summary> 14 public class Person:IStsObject 15 { 16 17 } 18 19 /// <summary> 20 /// My location class 21 /// </summary> 22 public class Location : IStsObject 23 { 24 25 } 26 27 /// <summary> 28 /// my loader interface 29 /// </summary> 30 public interface ILoader 31 { 32 Func<IStsObject> Load(); 33 } 34 35 /// <summary> 36 /// the loader class for my person object 37 /// </summary> 38 public class PersonLoader : ILoader 39 { 40 /// <summary> 41 /// creates instance of person class 42 /// </summary> 43 /// <returns></returns> 44 public Func<IStsObject> Load() 45 { 46 return () => new Person(); 47 } 48 } 49 50 51 52 /// <summary> 53 /// This class dtynamically creates instances of a class by using a loader class and functions. 54 /// </summary> 55 public static class FuncLoader 56 { 57 static Dictionary<string, Func<IStsObject>> dic = new Dictionary<string, Func<IStsObject>>(); 58 59 /// <summary> 60 /// Pre loads all functions into memory 61 /// </summary> 62 public static void LoadUp() 63 { 64 foreach (var x in DB.Read()) 65 { 66 var a = System.Activator.CreateInstance(x.AssemblyName, x.LoaderClassName); 67 var b = a.Unwrap() as ILoader; 68 69 var c = b.Load(); 70 71 dic.Add(x.ClassName, c); 72 } 73 } 74 75 /// <summary> 76 /// Creates instance of object from invoking function 77 /// </summary> 78 /// <param name="type"></param> 79 /// <returns></returns> 80 public static IStsObject GetObject(string type) 81 { 82 var f = dic[type]; 83 return f.Invoke(); 84 } 85 } 86 87 /// <summary> 88 /// This class dynamically creates objects by calling activator create instance on a type 89 /// </summary> 90 public static class TypeLoader 91 { 92 static Dictionary<string, Type> dic = new Dictionary<string, Type>(); 93 94 /// <summary> 95 /// Pre loads all types into memory 96 /// </summary> 97 public static void LoadUp() 98 { 99 foreach (var x in DB.Read()) 100 { 101 var a = System.Activator.CreateInstance(x.AssemblyName, x.ClassName); 102 var b = a.Unwrap() as IStsObject; 103 104 var c = b.GetType(); 105 106 dic.Add(x.ClassName, c); 107 108 } 109 } 110 111 /// <summary> 112 /// Create instance of object 113 /// </summary> 114 /// <param name="type"></param> 115 /// <returns></returns> 116 public static IStsObject GetObject(string type) 117 { 118 var f = dic[type]; 119 return (IStsObject)System.Activator.CreateInstance(f); 120 } 121 } 122 123 /// <summary> 124 /// Class simulating the database 125 /// </summary> 126 public static class DB 127 { 128 static List<DBRecord> dbValues; 129 130 /// <summary> 131 /// Populating the 'database' 132 /// </summary> 133 static DB() 134 { 135 dbValues = new List<DBRecord>(); 136 dbValues.Add(new DBRecord() { ClassName = "DynamicObjectLoader.Person", LoaderClassName = "DynamicObjectLoader.PersonLoader", AssemblyName = "DynamicObjectLoader" }); 137 } 138 139 /// <summary> 140 /// Simulate a read all from the database 141 /// </summary> 142 /// <returns></returns> 143 public static IEnumerable<DBRecord> Read() 144 { 145 return dbValues.AsEnumerable(); 146 } 147 148 /// <summary> 149 /// Simulate a read of a single class details from the database 150 /// </summary> 151 /// <param name="className"></param> 152 /// <returns></returns> 153 public static DBRecord Read(string className) 154 { 155 return dbValues.Where(d => d.ClassName == className).FirstOrDefault(); 156 } 157 } 158 159 /// <summary> 160 /// class simulating a record set from the database 161 /// </summary> 162 public class DBRecord 163 { 164 public string ClassName { get; set; } 165 public string LoaderClassName { get; set; } 166 public string AssemblyName { get; set; } 167 } The test program ran as:   1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var n = "DynamicObjectLoader.Person"; 6 7 TypeLoader.LoadUp(); 8 FuncLoader.LoadUp(); 9 10 var r4 = Test(TestCases.ControlTest(n)); 11 var r1 = Test(TestCases.Test1(n)); 12 var r2 = Test(TestCases.Test2(n)); 13 var r3 = Test(TestCases.Test3(n)); 14 15 Console.WriteLine(r1.TotalMilliseconds.ToString()); 16 Console.WriteLine(r2.TotalMilliseconds.ToString()); 17 Console.WriteLine(r3.TotalMilliseconds.ToString()); 18 Console.WriteLine(r4.TotalMilliseconds.ToString()); 19 20 Console.Read(); 21 } 22 23 static TimeSpan Test(Func<IStsObject> tester) 24 { 25 var sw = System.Diagnostics.Stopwatch.StartNew(); 26 int max = 1000000; 27 for (int i = 0; i < max; i++) 28 { 29 var myClass = tester.Invoke(); 30 } 31 sw.Stop(); 32 return new TimeSpan(sw.ElapsedTicks); 33 } 34 } The results for 1,000,000 object creations are as follows: Control: 8.5045ms Test 1: 5765.2321ms Test 2: 74.2969ms Test 3: 35.0375ms You can see how slow using CreateInstance(string, string) is every time, nearly 6 seconds to create the object a million times. What is interesting is that even though tests two and three are of the same magnitude, using Invoke() of CreateInstance(type) is twice as fast, a reasonable saving. Program.cs (9.33 kb)

Tags: ,

.net | c#

Month List

A little about us