(Continuing my previous post)
The heart of DCF is the Reflection-based bit that generates a helper class for each of the supported classes. That's all done in the init() method hereunder:
/// <summary>
/// Initializes the ClassFactory.
/// Creates the code for the ClassFactory and compiles it.
/// If for any reason the ClassFactory could not be properly created, an exception is fired.
/// </summary>
private static void init()
{
try
{
lock (items.SyncRoot)
{
CSharpCodeProvider cp = new CSharpCodeProvider();
//ICodeCompiler ic = cp.CreateCompiler();
CodeDomProvider cdp = new CSharpCodeProvider();
CompilerParameters cpar = new CompilerParameters();
cpar.GenerateInMemory = true;
cpar.GenerateExecutable = false;
Hashtable assemblies = new Hashtable();
// Add default references
cpar.ReferencedAssemblies.Add("system.dll");
cpar.ReferencedAssemblies.Add("DynamicClassFactory.dll");
foreach (DynamicClassFactoryItem item in items)
{
// Add the reference libraries to the compiler options
// This was added to support cases that referenced libraries are not located
// in the current directory. It became necessary when using the class factory
// from within a Service, in which case the current directory is not the
// directory of the currently running assembly (but System32)
// IMPORTANT: I you want to use this class from within a service, you must have
// entered the folders where to look for assemblies in the config file as so:
// <ReferenceLibs>
// <ReferenceLib>T:\Src\TSSrv\bin\Debug</ReferenceLib>
// </ReferenceLibs>
foreach (string refLib in item.ReferenceLibs)
{
cpar.CompilerOptions += " /lib:\"" + refLib + "\"";
}
// Load the assemblies - will enable us to get type-information
Assembly currentAssembly = (Assembly)assemblies[item.AssemblyName];
if (currentAssembly == null)
{
currentAssembly = Assembly.LoadFrom(item.AssemblyName);
assemblies[item.AssemblyName] = currentAssembly;
if (currentAssembly == null)
{
throw new Exception(string.Format("Assembly with name {0} could not be loaded by DynamicClassFactory", item.AssemblyName));
}
// Set references:
// 1. First we must add the current assembly to the references
// 2. Then we must add all referenced assemblies as well
// NOTE: We explicitely do NOT use cpar.ReferencedAssemblies because
// it does not handle well cases that the assembly name has blanks in it
cpar.CompilerOptions += " /reference:\"" + item.AssemblyName + "\"";
Debug.WriteLine("Adding " + currentAssembly.GetReferencedAssemblies().Length.ToString() + " referenced assemblies for " + item.AssemblyName);
foreach (AssemblyName assemblyName in currentAssembly.GetReferencedAssemblies())
{
if (!cpar.ReferencedAssemblies.Contains("\"" + assemblyName.Name + ".dll\""))
{
Debug.WriteLine("Adding reference assembly - \"" + assemblyName.Name + ".dll\"");
cpar.CompilerOptions += " /reference:\"" + assemblyName.Name + ".dll\"";
}
}
Debug.WriteLine("All referenced assemblies for " + item.AssemblyName + " were added successfully");
}
}
string newLine = Environment.NewLine;
string src =
"using System;" + newLine +
"using DynamicClassFactory;" + newLine +
"class ClassFactoryImpl : DynamicClassFactory.IClassFactory" + newLine +
"{" + newLine +
"public ClassFactoryImpl(){}" + newLine +
"public object CreateInstance(int id, params object[] args)" + newLine +
"{" + newLine +
"switch (id)" + newLine +
"{" + newLine;
foreach (DynamicClassFactoryItem item in items)
{
src += "case " + Convert.ToInt32(item.Key) + ":" + newLine +
"return " + item.ClassName.Replace(".", "_") + "ClassCreator.Create(args);" + newLine;
}
src += "default:" + newLine +
"return null;" + newLine +
"}" + newLine +
"}" + newLine;
// Create an internal class for each supported item in the factory.
foreach (DynamicClassFactoryItem item in items)
{
src += "class " + item.ClassName.Replace(".", "_") + "ClassCreator " + newLine +
"{" + newLine +
"public static object Create(params object[] args)" + newLine +
"{" + newLine;
Assembly a = (Assembly)assemblies[item.AssemblyName];
Type currentType = null;
foreach (Type t in a.GetTypes())
{
if (t.FullName == item.ClassName)
{
currentType = t;
break;
}
}
HybridDictionary parametersByLength = new HybridDictionary(currentType.GetConstructors().Length);
// First we organize the various ctor parameters according to the number of parameters in it
foreach (ConstructorInfo cInfo in currentType.GetConstructors())
{
ParameterInfo[] parameters = cInfo.GetParameters();
ArrayList parametersList = parametersByLength[parameters.Length] as ArrayList;
if (parametersList == null)
{
parametersList = new ArrayList();
parametersByLength[parameters.Length] = parametersList;
}
parametersList.Add(parameters);
}
// Now, for each size of ctor, we build simple if-predicates.
// This may not be the best-performing solution (e.g. if there are many ctors with
// 10 parameters and they differ only by the type of the last parameter),
// but in most cases it's more than enough.
// Another approach would be to build a decision tree, such that each possible ctor
// would end in one of the tree's leaves. However, it would significantly increase
// the implementation complexity, and would probably do little to improve performance.
foreach (int parametersCount in parametersByLength.Keys)
{
ArrayList parametersList = parametersByLength[parametersCount] as ArrayList;
src += "if (args.Length == " + parametersCount.ToString() + ")" + newLine +
"{" + newLine;
if (parametersCount == 0)
{
src += "return new " + item.ClassName + "();" + newLine;
}
else
{
foreach (ParameterInfo[] parameters in parametersList)
{
string argsList = "";
src += "if (";
for (int i = 0; i < parameters.Length; i++)
{
ParameterInfo pInfo = parameters[i];
src += "(args[" + i.ToString() + "] is " + pInfo.ParameterType.ToString() + ")";
if (i != parameters.Length-1)
{
src += " && ";
}
if (i != 0)
{
argsList += ", ";
}
argsList += "(" + pInfo.ParameterType.ToString() + ")args[" + i.ToString() + "]";
}
src += ")" + newLine +
"{" + newLine +
"return new " + item.ClassName + "(" + argsList + ");" + newLine +
"}" + newLine;
}
}
src += "}" + newLine;
}
src += "return null;" + newLine;
src += "}" + newLine +
"}" + newLine;
}
src += "}" + newLine;
Debug.WriteLine(src); // DO NOT DELETE THIS LINE - it prints the actual Class Factory to the Debug output
// Compile the actual implementation of the class factory
//CompilerResults cr = ic.CompileAssemblyFromSource(cpar,src);
CompilerResults cr = cdp.CompileAssemblyFromSource(cpar, src);
foreach (CompilerError ce in cr.Errors)
{
string errMsg = "DynamicClassFactory Compiler Error (nr. " + ce.ErrorNumber.ToString() +
") on line " + ce.Line.ToString() + ": " + ce.ErrorText;
Debug.WriteLine(errMsg);
EventLogWrapper.WriteEntry(EventType.Warning, errMsg);
}
if (cr.Errors.Count == 0 && cr.CompiledAssembly != null)
{
Type ObjType = cr.CompiledAssembly.GetType("ClassFactoryImpl");
try
{
if (ObjType != null)
{
instance = Activator.CreateInstance(ObjType) as IClassFactory;
}
}
catch (Exception ex)
{
logException("DynamicClassFactory exception when creating internal instance: ", ex);
}
}
}
}
catch (Exception e)
{
// If any exception occurred, send it to Debug and propagate it on - because the application
// won't be able to continue
logException("ClassFactory.init() generated an exception: ", e);
throw;
}
// If there is any error - throw an exception. The application won't be
// able to continue from here anyway...
if (instance == null)
{
throw new Exception("The dynamic ClassFactory could not be created.");
}
}
No comments:
Post a Comment