views:

294

answers:

6

I have a class which looks like this

public class Field
{
    public string FieldName;
    public string FieldType;

}

Based on an object List<Field> with values

{"EmployeeID","int"},
{"EmployeeName","String"},
{"Designation","String"}

I want to create a class that looks like this:

Class DynamicClass
{
    int EmployeeID,
    String EmployeeName,
    String Designation
}

}

Is there any way to do this?

EDIT: I want this to be generated in runtime. I dont want a physical CS file residing in my filesystem

+13  A: 

Yes, you can use System.Reflection.Emit namespace for this. It is not straight forward if you have no experience with it, but it is certainly possible.

Edit: This code might be flawed, but it will give you the general idea and hopefully off to a good start towards the goal.

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace TypeBuilderNamespace
{
    public static class MyTypeBuilder
    {
        public static void CreateNewObject()
        {
            var myType = CompileResultType();
            var myObject = Activator.CreateInstance(myType);
        }
        public static Type CompileResultType()
        {
            TypeBuilder tb = GetTypeBuilder();
            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
            foreach (var field in yourListOfFields)
                CreateProperty(tb, field.FieldName, field.FieldType);

            Type objectType = tb.CreateType();
            return objectType;
        }

        private static TypeBuilder GetTypeBuilder()
        {
            var typeSignature = "MyDynamicType";
            var an = new AssemblyName(typeSignature);
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , null);
            return tb;
        }

        private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
        {
            FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();

            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr =
                tb.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType });

            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();

            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);

            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }
}
danijels
Awesome!! Can you also tell me how to create an object of the type returned by the CompileResultType() Method?
ashwnacharya
You can use System.Activator for that. I'll update the answer with an example.
danijels
Note also that you will have to use reflection to examine, read and update fields in your dynamic type. If you want intellisense and no reflection, you have to have a static base class or interface that your dynamic class inherits from and can be casted to. In that case you can modify GetTypeBuilder() method and change moduleBuilder.DefineType call to include the static type as the last parameter (is null now)
danijels
+2  A: 

You want to look at CodeDOM. It allows defining code elements and compiling them. Quoting MSDN:

...This object graph can be rendered as source code using a CodeDOM code generator for a supported programming language. The CodeDOM can also be used to compile source code into a binary assembly.

Hemant
I want this to be generated in runtime. I dont want a physical CS file residing in my filesystem. Sorry for not mentioning that earlier.
ashwnacharya
@ashwnacharya: You **can** use CodeDOM for both generating the source file and compiling it at runtime!
Hemant
Wrong link for CodeDOM, please change it to http://msdn.microsoft.com/en-us/library/y2k85ax6.aspx?assembly.
Ramon Araujo
Beware though, that the CodeDOM compiler takes a raw string, and thus you might want to consider "code insertion attacks" similar to those used in XSS and SQL injection.
cwap
@Ramon: Thanks for pointing out. Fixed the link.
Hemant
+1  A: 

You can look at using dynamic modules and classes that can do the job. The only disadvantage is that it remains loaded in the app domain. But with the version of .NET framework being used, that could change. .NET 4.0 supports collectible dynamic assemblies and hence you can recreate the classes/types dynamically.

Saravanan
A: 

Runtime Code Generation with JVM and CLR - Peter Sestoft

Work for persons that are really interested in this type of programming.

EDIT:

My tip for You is that if You declare something try to avoid string, so if You have class Field it is better to use class System.Type to store the field type than a string. And for the sake of best solutions instead of creation new classes try to use those that has been created FiledInfo instead of creation new.

Vash
+1  A: 

It will take some work, but is certainly not impossible.

What I have done is:

  • Create a C# source in a string (no need to write out to a file),
  • Run it through the Microsoft.CSharp.CSharpCodeProvider (CompileAssemblyFromSource)
  • Find the generated Type
  • And create an instance of that Type (Activator.CreateInstance)

This way you can deal with the C# code you already know, instead of having to emit MSIL.

But this works best if your class implements some interface (or is derived from some baseclass), else how is the calling code (read: compiler) to know about that class that will be generated at runtime?

Hans Kesting
Any particular reason for the downvote?
Hans Kesting
upvoted since you don't deserve the downvote.
jgauffin
+1: This can be a good approach in the right circumstances.
Ani
+1  A: 

I don't know the intended usage of such dynamic classes, and code generation and run time compilation can be done, but takes some effort. Maybe Anonymous Types would help you, something like:

var v = new { EmployeeID = 108, EmployeeName = "John Doe" };
Amittai Shapira