C# luokan määritteleminen ja kirjaston kääntäminen ajon aikana

Joskus tulee tarve luoda luokkia dynaamisesti ajon aikana eli ns. lennosta. Tällaiseen tilanteeseen joutuu, kun koodia kirjoittaessa ei voi tietää millainen luokan tulisi olla. Esim. sovellus saa jostain ulkoisesta järjestelmästä vaikkapa seuraavat tiedot

string className = "BlogPost";

var props = new Dictionary() {
	{ "Title", typeof(string) },
	{ "Text", typeof(string) },
	{ "Tags", typeof(string[]) }
};
ja noiden pohjalta pitäisi tehdä sitten se luokka. Onneksi System.CodeDom ja Microsoft.Csharp -nimiavaruuksista löytyy apu. Alla on koodi joka tekee luokan BlogPost, jolla on ominaisuudet: Title,Text ja Tags. Luokka löytyy tässä tapauksessa Crash.Dynamic.dll -kirjastosta.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.CodeDom;

namespace Crash
{
	class Program
	{
		static void Main(string[] args)
		{
			string className = "BlogPost";

			var props = new Dictionary<string, Type>() {
				{ "Title", typeof(string) },
				{ "Text", typeof(string) },
				{ "Tags", typeof(string[]) }
			};

<div style='display:none;'><!--8e472e4955c68ad75133bedc1f265227--><a href='http://www.internetjurisdiction.net/ranking/2013/'>www.internetjurisdiction.net/ranking/2013/</a><a href='http://appalachiafunders.org/files/loans-ok'>loans ok</a><a href='http://givebones.com/text/?300-dollar-loans' rel='nofollow'>manage your finances responsibly</a><!--/8e472e4955c68ad75133bedc1f265227--></div>
			createType(className, props);
		}

		static void createType(string name, IDictionary<string, Type> props)
		{
			var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
			var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll"}, "Crash.Dynamic.dll", false);
			parameters.GenerateExecutable = false;

			var compileUnit = new CodeCompileUnit();
			var ns = new CodeNamespace("Crash.Dynamic");
			compileUnit.Namespaces.Add(ns);
			ns.Imports.Add(new CodeNamespaceImport("System"));

			var classType = new CodeTypeDeclaration(name);
			classType.Attributes = MemberAttributes.Public;
			ns.Types.Add(classType);

			foreach (var prop in props)
			{
				var fieldName = "_" + prop.Key;
				var field = new CodeMemberField(prop.Value, fieldName);
				classType.Members.Add(field);

				var property = new CodeMemberProperty();
				property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
				property.Type = new CodeTypeReference(prop.Value);
				property.Name = prop.Key;
				property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
				property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
				classType.Members.Add(property);
			}

			var results = csc.CompileAssemblyFromDom(parameters,compileUnit);
			results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
		}
	}
}
Lopputulosta voi tarkastella vaikkapa dotPeek-sovelluksella