How to dynamically create a class?
Asked Answered
E

16

266

I have a class which looks like this:

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

And 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?

I want this to be generated at runtime. I don't want a physical CS file residing in my filesystem.

Eldon answered 5/10, 2010 at 9:4 Comment(6)
Do You want to use that class in runtime or only generate file ?Kokoruda
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.Eldon
Can you give us a rough idea of what do you intend to do with this class?Celisse
@Celisse implement runtime-resolved interfaces, for instance.Simonides
One could feed it to System.ServiceModel.ChannelFactory<MyDynamicInterface>Damnation
I am not sure why do you need a dynamic class if you say anonymous object that makes some sense. But what is the purpose of a dynamic class here, i mean what are you attempting to achieve ? Plz see my answer of DynamicEntity here https://mcmap.net/q/108799/-how-to-dynamically-create-a-classFilterable
H
345

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);
        }
    }
}
Helyn answered 5/10, 2010 at 9:6 Comment(18)
Awesome!! Can you also tell me how to create an object of the type returned by the CompileResultType() Method?Eldon
You can use System.Activator for that. I'll update the answer with an example.Helyn
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)Helyn
can someone explain how to use the object after its createdJubbulpore
@bugz use the code above to create the class, then in the base class you can add this method: public void SetValue<T>(string name, T value) { GetType().GetProperty(name).SetValue(this, value); }Laurentia
is there a way to check if it already exists in the assembly before callingPerlman
@user2330270 Yes you can use Type.GetType(string) see linkCeric
Apart from what @Helyn suggested to use a base interface is there any other way to pass this newly generated type as the base type for generic class? i.e. T in List<T>Ceric
@Helyn This code worked well for me, but I also want to create the physical class file for this class, which can be used in project. How can I do this?Metalware
@mohammad-sohail you should have a look at some of the other answers for that. Not what OP asked for, thoughHelyn
Hi what if we want to update the PropertyInfo instead of creating a new PropertyConsonance
how can i use this runtime class in list<T> use it?Ornie
For the sake of completeness, I just want to add that you can also work with such types using the dynamic keyword, although if you know member names at compile time, it is better to have them implement an interface or inherit a base class and access through that. Still... completeness.Varian
you seems really good with IL could u please help me with this #48161489Consonance
how do i unregister / remove the newly defined type? im trying to create a lot of classes on the fly. Want them GC'ed after use.Freeman
we need to use AssemblyBuilderAccess.RunAndCollect to improve memory consumption.Freeman
Will that return a dynamic class or a generic class that preserves type safety?Scamander
I have used this in .net core and class is created but its showing value of namespace is nullInfirmity
W
77

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?

Waverly answered 5/10, 2010 at 9:21 Comment(2)
Might want to see this discussion: reflection-emit-vs-codedomBakemeier
@Bakemeier , ashwnacharya, wanted to generate dynamically at runtime its class with its members originally contained into a list. I don't think putting it into a file even generated at runtime would be a good performance solution.Nighttime
L
46

You can also dynamically create a class by using DynamicObject.

public class DynamicClass : DynamicObject
{
    private Dictionary<string, KeyValuePair<Type, object>> _fields;

    public DynamicClass(List<Field> fields)
    {
        _fields = new Dictionary<string, KeyValuePair<Type, object>>();
        fields.ForEach(x => _fields.Add(x.FieldName,
            new KeyValuePair<Type, object>(x.FieldType, null)));
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_fields.ContainsKey(binder.Name))
        {
            var type = _fields[binder.Name].Key;
            if (value.GetType() == type)
            {
                _fields[binder.Name] = new KeyValuePair<Type, object>(type, value);
                return true;
            }
            else throw new Exception("Value " + value + " is not of type " + type.Name);
        }
        return false;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = _fields[binder.Name].Value;
        return true;
    }
}

I store all class fields in a dictionary _fields together with their types and values. The both methods are to can get or set value to some of the properties. You must use the dynamic keyword to create an instance of this class.

The usage with your example:

var fields = new List<Field>() { 
    new Field("EmployeeID", typeof(int)),
    new Field("EmployeeName", typeof(string)),
    new Field("Designation", typeof(string)) 
};

dynamic obj = new DynamicClass(fields);

//set
obj.EmployeeID = 123456;
obj.EmployeeName = "John";
obj.Designation = "Tech Lead";

obj.Age = 25;             //Exception: DynamicClass does not contain a definition for 'Age'
obj.EmployeeName = 666;   //Exception: Value 666 is not of type String

//get
Console.WriteLine(obj.EmployeeID);     //123456
Console.WriteLine(obj.EmployeeName);   //John
Console.WriteLine(obj.Designation);    //Tech Lead

Edit: And here is how looks my class Field:

public class Field
{
    public Field(string name, Type type)
    {
        this.FieldName = name;
        this.FieldType = type;
    }

    public string FieldName;

    public Type FieldType;
}
Lag answered 9/1, 2016 at 2:25 Comment(2)
I liked this approach till I needed to initialize the fields with the constructor. i.e dynamic obj = new DynamicClass(fields){EmployeeId=123456;EmployeeName = "John"; Designation = "Tech Lead";} It would really be great doing this.Staffan
Also note that this approach forces boxing for all value type fields.Ellita
G
28

I know i reopen this old task but with c# 4.0 this task is absolutely painless.

dynamic expando = new ExpandoObject();
expando.EmployeeID=42;
expando.Designation="unknown";
expando.EmployeeName="curt"

//or more dynamic
AddProperty(expando, "Language", "English");

for more see https://www.oreilly.com/learning/building-c-objects-dynamically

Gaea answered 11/1, 2018 at 12:17 Comment(2)
Yes, but you lose type safety here. Can we do something similar while preserving type safety?Scamander
use in conjunction with https://mcmap.net/q/99936/-dynamically-adding-properties-to-an-expandoobjectOrient
T
18

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" };
Tutt answered 5/10, 2010 at 10:1 Comment(2)
You can't hard code the field names. He's providing them with his Field.FieldName. Having to hard code the field names defeats the purpose. If you have to do that, you might as well create the class.Li
I've used these for years and forgot what they were called, Anonymous Types. Thanks!Jarrell
H
13

As Hans suggested, you can use Roslyn to dynamically create classes.

Full source:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace RoslynDemo1
{
    class Program
    {
        static void Main(string[] args)
        {
            var fields = new List<Field>()
            {
                new Field("EmployeeID","int"),
                new Field("EmployeeName","String"),
                new Field("Designation","String")
            };

            var employeeClass = CreateClass(fields, "Employee");

            dynamic employee1 = Activator.CreateInstance(employeeClass);
            employee1.EmployeeID = 4213;
            employee1.EmployeeName = "Wendy Tailor";
            employee1.Designation = "Engineering Manager";

            dynamic employee2 = Activator.CreateInstance(employeeClass);
            employee2.EmployeeID = 3510;
            employee2.EmployeeName = "John Gibson";
            employee2.Designation = "Software Engineer";

            Console.WriteLine($"{employee1.EmployeeName}");
            Console.WriteLine($"{employee2.EmployeeName}");

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        public static Type CreateClass(List<Field> fields, string newClassName, string newNamespace = "Magic")
        {
            var fieldsCode = fields
                                .Select(field => $"public {field.FieldType} {field.FieldName};")
                                .ToString(Environment.NewLine);

            var classCode = $@"
                using System;

                namespace {newNamespace}
                {{
                    public class {newClassName}
                    {{
                        public {newClassName}()
                        {{
                        }}

                        {fieldsCode}
                    }}
                }}
            ".Trim();

            classCode = FormatUsingRoslyn(classCode);


            var assemblies = new[]
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            };

            /*
            var assemblies = AppDomain
                        .CurrentDomain
                        .GetAssemblies()
                        .Where(a => !string.IsNullOrEmpty(a.Location))
                        .Select(a => MetadataReference.CreateFromFile(a.Location))
                        .ToArray();
            */

            var syntaxTree = CSharpSyntaxTree.ParseText(classCode);

            var compilation = CSharpCompilation
                                .Create(newNamespace)
                                .AddSyntaxTrees(syntaxTree)
                                .AddReferences(assemblies)
                                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);
                //compilation.Emit($"C:\\Temp\\{newNamespace}.dll");

                if (result.Success)
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    Assembly assembly = Assembly.Load(ms.ToArray());

                    var newTypeFullName = $"{newNamespace}.{newClassName}";

                    var type = assembly.GetType(newTypeFullName);
                    return type;
                }
                else
                {
                    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error);

                    foreach (Diagnostic diagnostic in failures)
                    {
                        Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                    }

                    return null;
                }
            }
        }

        public static string FormatUsingRoslyn(string csCode)
        {
            var tree = CSharpSyntaxTree.ParseText(csCode);
            var root = tree.GetRoot().NormalizeWhitespace();
            var result = root.ToFullString();
            return result;
        }
    }

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

        public Field(string fieldName, string fieldType)
        {
            FieldName = fieldName;
            FieldType = fieldType;
        }
    }

    public static class Extensions
    {
        public static string ToString(this IEnumerable<string> list, string separator)
        {
            string result = string.Join(separator, list);
            return result;
        }
    }
}
Hint answered 12/9, 2020 at 14:41 Comment(0)
S
10

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.

Spector answered 5/10, 2010 at 9:6 Comment(3)
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.Eldon
@ashwnacharya: You can use CodeDOM for both generating the source file and compiling it at runtime!Spector
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.Renettarenew
D
10

You can also dynamically create a class by using DynamicExpressions.

Since 'Dictionary's have compact initializers and handle key collisions, you will want to do something like this.

  var list = new Dictionary<string, string> {
    {
      "EmployeeID",
      "int"
    }, {
      "EmployeeName",
      "String"
    }, {
      "Birthday",
      "DateTime"
    }
  };

Or you might want to use a JSON converter to construct your serialized string object into something manageable.

Then using System.Linq.Dynamic;

  IEnumerable<DynamicProperty> props = list.Select(property => new DynamicProperty(property.Key, Type.GetType(property.Value))).ToList();

  Type t = DynamicExpression.CreateClass(props);

The rest is just using System.Reflection.

  object obj = Activator.CreateInstance(t);
  t.GetProperty("EmployeeID").SetValue(obj, 34, null);
  t.GetProperty("EmployeeName").SetValue(obj, "Albert", null);
  t.GetProperty("Birthday").SetValue(obj, new DateTime(1976, 3, 14), null);
}  
Davey answered 6/10, 2017 at 21:31 Comment(0)
P
8

Based on @danijels's answer, dynamically create a class in VB.NET:

Imports System.Reflection
Imports System.Reflection.Emit

Public Class ObjectBuilder

Public Property myType As Object
Public Property myObject As Object

Public Sub New(fields As List(Of Field))
    myType = CompileResultType(fields)
    myObject = Activator.CreateInstance(myType)
End Sub

Public Shared Function CompileResultType(fields As List(Of Field)) As Type
    Dim tb As TypeBuilder = GetTypeBuilder()
    Dim constructor As ConstructorBuilder = tb.DefineDefaultConstructor(MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.RTSpecialName)

    For Each field In fields
        CreateProperty(tb, field.Name, field.Type)
    Next

    Dim objectType As Type = tb.CreateType()
    Return objectType
End Function

Private Shared Function GetTypeBuilder() As TypeBuilder
    Dim typeSignature = "MyDynamicType"
    Dim an = New AssemblyName(typeSignature)
    Dim assemblyBuilder As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run)
    Dim moduleBuilder As ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule")
    Dim tb As TypeBuilder = moduleBuilder.DefineType(typeSignature, TypeAttributes.[Public] Or TypeAttributes.[Class] Or TypeAttributes.AutoClass Or TypeAttributes.AnsiClass Or TypeAttributes.BeforeFieldInit Or TypeAttributes.AutoLayout, Nothing)
    Return tb
End Function

Private Shared Sub CreateProperty(tb As TypeBuilder, propertyName As String, propertyType As Type)
    Dim fieldBuilder As FieldBuilder = tb.DefineField("_" & propertyName, propertyType, FieldAttributes.[Private])

    Dim propertyBuilder As PropertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, Nothing)
    Dim getPropMthdBldr As MethodBuilder = tb.DefineMethod("get_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, propertyType, Type.EmptyTypes)
    Dim getIl As ILGenerator = getPropMthdBldr.GetILGenerator()

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

    Dim setPropMthdBldr As MethodBuilder = tb.DefineMethod("set_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, Nothing, {propertyType})

    Dim setIl As ILGenerator = setPropMthdBldr.GetILGenerator()
    Dim modifyProperty As Label = setIl.DefineLabel()
    Dim exitSet As Label = 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)
End Sub

End Class
Perorate answered 30/5, 2013 at 21:28 Comment(0)
G
8

For those wanting to create a dynamic class just properties (i.e. POCO), and create a list of this class. Using the code provided later, this will create a dynamic class and create a list of this.

var properties = new List<DynamicTypeProperty>()
{
    new DynamicTypeProperty("doubleProperty", typeof(double)),
    new DynamicTypeProperty("stringProperty", typeof(string))
};

// create the new type
var dynamicType = DynamicType.CreateDynamicType(properties);
// create a list of the new type
var dynamicList = DynamicType.CreateDynamicList(dynamicType);

// get an action that will add to the list
var addAction = DynamicType.GetAddAction(dynamicList);

// call the action, with an object[] containing parameters in exact order added
addAction.Invoke(new object[] {1.1, "item1"});
addAction.Invoke(new object[] {2.1, "item2"});
addAction.Invoke(new object[] {3.1, "item3"});

Here are the classes that the previous code uses.

Note: You'll also need to reference the Microsoft.CodeAnalysis.CSharp library.

       /// <summary>
    /// A property name, and type used to generate a property in the dynamic class.
    /// </summary>
    public class DynamicTypeProperty
    {
        public DynamicTypeProperty(string name, Type type)
        {
            Name = name;
            Type = type;
        }
        public string Name { get; set; }
        public Type Type { get; set; }
    }

   public static class DynamicType
    {
        /// <summary>
        /// Creates a list of the specified type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<object> CreateDynamicList(Type type)
        {
            var listType = typeof(List<>);
            var dynamicListType = listType.MakeGenericType(type);
            return (IEnumerable<object>) Activator.CreateInstance(dynamicListType);
        }

        /// <summary>
        /// creates an action which can be used to add items to the list
        /// </summary>
        /// <param name="listType"></param>
        /// <returns></returns>
        public static Action<object[]> GetAddAction(IEnumerable<object> list)
        {
            var listType = list.GetType();
            var addMethod = listType.GetMethod("Add");
            var itemType = listType.GenericTypeArguments[0];
            var itemProperties = itemType.GetProperties();

            var action = new Action<object[]>((values) =>
            {
                var item = Activator.CreateInstance(itemType);

                for(var i = 0; i < values.Length; i++)
                {
                    itemProperties[i].SetValue(item, values[i]);
                }

                addMethod.Invoke(list, new []{item});
            });

            return action;
        }

        /// <summary>
        /// Creates a type based on the property/type values specified in the properties
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Type CreateDynamicType(IEnumerable<DynamicTypeProperty> properties)
        {
            StringBuilder classCode = new StringBuilder();

            // Generate the class code
            classCode.AppendLine("using System;");
            classCode.AppendLine("namespace Dexih {");
            classCode.AppendLine("public class DynamicClass {");

            foreach (var property in properties)
            {
                classCode.AppendLine($"public {property.Type.Name} {property.Name} {{get; set; }}");
            }
            classCode.AppendLine("}");
            classCode.AppendLine("}");

            var syntaxTree = CSharpSyntaxTree.ParseText(classCode.ToString());

            var references = new MetadataReference[]
            {
                MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
                MetadataReference.CreateFromFile(typeof(DictionaryBase).GetTypeInfo().Assembly.Location)
            };

            var compilation = CSharpCompilation.Create("DynamicClass" + Guid.NewGuid() + ".dll",
                syntaxTrees: new[] {syntaxTree},
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);

                if (!result.Success)
                {
                    var failures = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error);

                    var message = new StringBuilder();

                    foreach (var diagnostic in failures)
                    {
                        message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                    }

                    throw new Exception($"Invalid property definition: {message}.");
                }
                else
                {

                    ms.Seek(0, SeekOrigin.Begin);
                    var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
                    var dynamicType = assembly.GetType("Dexih.DynamicClass");
                    return dynamicType;
                }
            }
        }
    }
Ground answered 18/4, 2019 at 2:51 Comment(1)
Great piece of code. Would be possible to use also AddRange on the dynamic list, to eventually add several records at a time?Kletter
C
3

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.

Courtyard answered 5/10, 2010 at 9:11 Comment(0)
T
2

Wow! Thank you for that answer! I added some features to it to create a "datatable to json" converter that I share with you.

    Public Shared Sub dt2json(ByVal _dt As DataTable, ByVal _sb As StringBuilder)
    Dim t As System.Type

    Dim oList(_dt.Rows.Count - 1) As Object
    Dim jss As New JavaScriptSerializer()
    Dim i As Integer = 0

    t = CompileResultType(_dt)

    For Each dr As DataRow In _dt.Rows
        Dim o As Object = Activator.CreateInstance(t)

        For Each col As DataColumn In _dt.Columns
            setvalue(o, col.ColumnName, dr.Item(col.ColumnName))
        Next

        oList(i) = o
        i += 1
    Next

    jss = New JavaScriptSerializer()
    jss.Serialize(oList, _sb)


End Sub

And in "compileresulttype" sub, I changed that:

    For Each column As DataColumn In _dt.Columns
        CreateProperty(tb, column.ColumnName, column.DataType)
    Next


Private Shared Sub setvalue(ByVal _obj As Object, ByVal _propName As String, ByVal _propValue As Object)
    Dim pi As PropertyInfo
    pi = _obj.GetType.GetProperty(_propName)
    If pi IsNot Nothing AndAlso pi.CanWrite Then
        If _propValue IsNot DBNull.Value Then
            pi.SetValue(_obj, _propValue, Nothing)

        Else
            Select Case pi.PropertyType.ToString
                Case "System.String"
                    pi.SetValue(_obj, String.Empty, Nothing)
                Case Else
                    'let the serialiser use javascript "null" value.
            End Select

        End If
    End If

End Sub
Twum answered 25/7, 2012 at 18:44 Comment(0)
S
2

you can use CSharpProvider:

var code = @"
    public class Abc {
       public string Get() { return ""abc""; }
    }
";

var options = new CompilerParameters();
options.GenerateExecutable = false;
options.GenerateInMemory = false;

var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(options, code);

var type = compile.CompiledAssembly.GetType("Abc");
var abc = Activator.CreateInstance(type);

var method = type.GetMethod("Get");
var result = method.Invoke(abc, null);

Console.WriteLine(result); //output: abc
Sickroom answered 18/9, 2020 at 16:51 Comment(0)
B
0

You can use System.Runtime.Remoting.Proxies.RealProxy. It will allow you to use "normal" code rather than low level assembly type stuff.

See the RealProxy answer to this question for a good example:

How do I intercept a method call in C#?

Bizerte answered 24/11, 2015 at 22:15 Comment(0)
F
0

I see 3 questions here:

1. I want to create a class that looks like this: Is there any way to do this ?

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

Yes, this is possible but the purpose should not be to make a class look like something but to work, behave and hold the data like this. And this is achievable whiteout using CodeGeneration, "T4 Template, dynamic, ExpandoObject, CSharpProvider or AnonymousObject.


2. I want this to be generated at runtime.

Generating a class at runtime won't do anything if it doesn't create a instance object.

And if I understand you are trying to save Employee data where the size of List<Field> is unbound and the best you can think of is to generate a class to hold that unbounded data dynamically at runtime.


Creating DynamicEntity

If that is your issue you can attempt to create Employee as a Entity like below called as DynamicEntity where the properties are available as a Dictionary<string,object> so it can be extended with new attributes/fields without the need of a new class design and it's not limited to 2-3 fields for employee.

public class DynamicEntity
{
    public string Name {get;private set;}
    public Dictionary<string,object> Attributes;
    
    public DynamicEntity(string name)
    {
        Name = name;
        Attributes = new Dictionary<string,object>();
        Attributes.Add("LogicalName",name);
        Attributes.Add("Id",Guid.NewGuid());
    }
    
    public object this[string key]
    {
        get {
            return Attributes[key];
        }
        
        set{
            Attributes[key] = value;
        }
    }
    
    public void AddAttribute<T>(string attributeName,T data)
    {
        Attributes.Add(attributeName,data);
    }
    
    public void SetAttributeValue<T>(string attributeName,T data)
    {
        Attributes[attributeName] = data;
    }
    
    public T GetAttributeValue<T>(string attributeName)
    {
        return (T)Attributes[attributeName];
    }
}

To use you can create an object at runtime and read/write/append attributes to it.

    var u = new DynamicEntity("employee"); //new object
    
    u.AddAttribute<int>("EmployeeID",10); //Add Id
    u["EmployeeName"]= "vinod"; //Add Name
    u["Designation"] = "hacker"; //Add Designation
    
    u.AddAttribute<string>("Address","India"); //Add New Attribute 
    
    u.SetAttributeValue<string>("EmployeeName","Vinod Srivastav");
    
    var id = u.GetAttributeValue<int>("EmployeeID"); //Read Attribute

If everything goes well you can get a object like follows:

enter image description here

Filterable answered 3/4, 2023 at 13:54 Comment(0)
L
-1

Runtime Code Generation with JVM and CLR - Peter Sestoft

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

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.

Latterday answered 5/10, 2010 at 9:20 Comment(3)
Link is dead: -1Spectrophotometer
Glenn: a quick googling revealed a working link: pdfs.semanticscholar.org/326a/…Bohun
@AndreasPardeike Thanks, I repaired it in the article.Spectrophotometer

© 2022 - 2024 — McMap. All rights reserved.