Creating a new class at runtime

Topics: Metadata Model, PDB Writer, PE Writer
Jan 20, 2011 at 3:42 PM

Suppose I have an application that contains an interface, IFoo. At runtime I'd like to create a new class that implements IFoo, and then create an instance of this new class to bind to an existing variable of type IFoo. If I wanted to use CCI to do this would I need to create a new assembly, write it to disk, and then load this assembly just like I'd load existing assemblies? Or is it possible to "inject" the new class into an existing loaded assembly? Anyone done something like this, or have any suggestions for the best way to proceed when using CCI? Does anyone know of any examples on the Internet that do something similar that might be useful as a starting point?

Thanks,

Kevin

Coordinator
Jan 20, 2011 at 4:14 PM

Currently, you'd have to write a lot of code to this. I have a backburner activity to help make this easier, but it may be another week or two before I get enough time to finish it.

Jan 20, 2011 at 9:08 PM

Thanks for the response Herman.  It's good to hear you are doing some work in this area.  I look forward to seeing the results.

Kevin

Feb 16, 2011 at 11:39 AM

Do you mean that is actually possible to extend a loaded CLR assembly?

Coordinator
Apr 20, 2011 at 6:56 PM

There are some things you can do with the Edit&Continue API for any assembly, but my assumption is that in general you'd have to load your disk assembly into a dynamic assembly. You can add classes at runtime to a dynamic assembly.

For this particular scenario it seems preferable to just load the disk assembly as normal and then to inject the adaptor classes into a dynamic assembly.

Since System.Reflection.Emit is a somewhat difficult API to use, I have now provided a way to load a CCI assembly as a dynamic assembly. As yet this is just a dump of what is on my machine. It compiles but I have not executed a single line of its code yet.

Apr 30, 2011 at 4:18 PM

Herman has an almost complete api for generating RunAndCollect assemblies. So what that would mean is that you could generate a new assembly for your implementation of IFoo but it wouldn't necessarily every exist on disk. Also if you drop all references to it, the assembly will be unloaded collected by the GC (except in some edge cases). But in my opinion, creating the metadata from scratch is a pretty intensive process. I realize there are a lot of concerns trying to be solved by cci but I wish the api for creating metadata and ast from scratch was simpler. 

May 1, 2011 at 1:02 PM

@justinc, I agree that the api is complex, but since CCI is targeting completeness and not specific programming scenarioes, this seems inevitable.  This was my motivation for creating Afterthought, as I found myself writing multiple similar transformations using CCI and felt both I and others would benefit from a simpler and more natural way of writing code into assemblies.  This is similar to how Microsoft introduced DbContext on top of the existing ObjectContext for the EntityFramework to create a better API surface for certain approaches.

I could see someone creating a simpler API on top of CCI for pure injection/creation of new code/assemblies.  Afterthought errs on the side of programmer efficiency and debugability by not allowing developers to actually emit code, but instead emits calls to existing static methods in other assemblies (anonymous delegates without closures, for example) in a way that is very similar to extension methods in C#.  For example, the following code from Afterthought uses CCI to modify a type to implement an interface:

// Implement IMath interface
ImplementInterface<IMath>(

	// Pi
	new Property<decimal>("Pi") { Getter = (instance, property) => 3.14159m },

	// Subtract()
	Method.Create("Subtract", (T instance, decimal x, decimal y) => x - y)
);