How to emit a Type in .NET Core
Asked Answered
C

1

41

In C#, how do I emit a new Type at runtime with .NET Core? All of the examples I can find for .NET 6 don't seem to work in .NET core (they all begin with getting the current AppDomain, which doesn't exist in .NET core any more).

If possible I would appreciate an example that involves creating a Type and adding a property to the Type.

Caldera answered 21/1, 2017 at 20:54 Comment(0)
D
71

Here is SO post about creating a dynamic type in .NET 4.

How to dynamically create a class in C#?

And in the accepted answer is just one use of AppDomain.

AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);

Here is another SO post about a replacement of DefineDynamicAssembly function in .NET core.

Is there any replace of AssemblyBuilder.DefineDynamicAssembly in .NET Core?

Here it is:

AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()),AssemblyBuilderAccess.Run);

In "System.Reflection.Emit": "4.0.1" nuget.

Also, there is a difference in TypeBuilder. CreateType function no longer exist, instead for creating types we should use CreateTypeInfo. And yes, it is again from SO post.

CreateType missing from TypeBuilder. How to port this?

Here is working modified example (for .NET core) of creating a Type and adding properties to the Type.

using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Reflection;

namespace ConsoleApp1
{
    public class FieldDescriptor
    {
        public FieldDescriptor(string fieldName, Type fieldType)
        {
            FieldName = fieldName;
            FieldType = fieldType;
        }
        public string FieldName { get; }
        public Type FieldType { get;  }
    }

    public static class MyTypeBuilder
    {
        public static object CreateNewObject()
        {
            var myTypeInfo = CompileResultTypeInfo();
            var myType = myTypeInfo.AsType();
            var myObject = Activator.CreateInstance(myType);

            return myObject;
        }

        public static TypeInfo CompileResultTypeInfo()
        {
            TypeBuilder tb = GetTypeBuilder();
            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            var yourListOfFields = new List<FieldDescriptor>()
            {
                new FieldDescriptor("YourProp1",typeof(string)),
                new FieldDescriptor("YourProp2", typeof(int))
            };
            foreach (var field in yourListOfFields)
                CreateProperty(tb, field.FieldName, field.FieldType);

            TypeInfo objectTypeInfo = tb.CreateTypeInfo();
            return objectTypeInfo;
        }

        private static TypeBuilder GetTypeBuilder()
        {
            var typeSignature = "MyDynamicType";
            var an = new AssemblyName(typeSignature);
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), 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);
        }
    }
}
Distend answered 21/1, 2017 at 22:22 Comment(4)
I am getting The type or namespace name 'FieldDescriptor' could not be found (are you missing a using directive or an assembly reference?. I am using .net core 3.1. Any ideas?Rida
@Rida did you copy the FieldDescriptor class, it is custom-defined class in the sample.Distend
Thanks @AramKocharyan. I figured that out after not finding the class on Microsoft site.Rida
Why the NOP in the IL code for the set methods? Also why have labels at all?Omniscience

© 2022 - 2024 — McMap. All rights reserved.