Search code examples
c#.netcilreflection.emit

Dynamic type shadow base class's property and set to protected using Reflection.Emit


I work for several days try to shadow base class's property and set the derived class property to protected using Reflection.Emit. When I create a derived class and set new to the base property, call GetProperties() it only show one property with the name and the derived class property is not public, but the dynamic type call GetProperties() show two properties with the same name appear(base property is public and dynamic type is not public). here is my code.

namespace ILHiddenProperty
{
    interface IILName
    {
        string Name { get; set; }
    }

    public class ClassName : IILName
    {
        public string Name { get; set; } = "ClassName";
    }

    public class ChildName : ClassName
    {
        protected new string Name { get; set; } = "ChildName";
    }

    public class NameILGenerator
    {
        public static Type ILType()
        {
            AssemblyName aname = new AssemblyName("MyAssembly");
            AppDomain currentDomain = AppDomain.CurrentDomain; // Thread.GetDomain();
            AssemblyBuilder asmbuilder = currentDomain.DefineDynamicAssembly(aname,
                                       AssemblyBuilderAccess.Run);
            ModuleBuilder mbuilder = asmbuilder.DefineDynamicModule("EmitMethods");
            TypeBuilder tbuilder = mbuilder.DefineType("ILName", TypeAttributes.Public,typeof(ClassName)); 
            FieldBuilder fName = tbuilder.DefineField("_name", typeof(System.String), FieldAttributes.Private);
            PropertyBuilder pName = tbuilder.DefineProperty("Name", PropertyAttributes.HasDefault, typeof(System.String), null);
            //Getter
            MethodBuilder mNameGet = tbuilder.DefineMethod("get_Name", MethodAttributes.Family | 
                MethodAttributes.SpecialName | 
                MethodAttributes.HideBySig, 
                typeof(System.String), 
                Type.EmptyTypes);
            ILGenerator nameGetIL = mNameGet.GetILGenerator();
            nameGetIL.Emit(OpCodes.Ldarg_0);
            nameGetIL.Emit(OpCodes.Ldfld, fName);
            nameGetIL.Emit(OpCodes.Ret);

            //Setter
            MethodBuilder mNameSet = tbuilder.DefineMethod("set_Name", MethodAttributes.Family | 
                MethodAttributes.SpecialName | 
                MethodAttributes.HideBySig, 
                null, 
                new Type[] { typeof(System.String) });

            ILGenerator nameSetIL = mNameSet.GetILGenerator();

            nameSetIL.Emit(OpCodes.Ldarg_0);
            nameSetIL.Emit(OpCodes.Ldarg_1);
            nameSetIL.Emit(OpCodes.Stfld, fName);
            nameSetIL.Emit(OpCodes.Ret);

            pName.SetGetMethod(mNameGet);
            pName.SetSetMethod(mNameSet);
            return tbuilder.CreateType();
        }
    }
}
namespace ILHiddenProperty
{
    class Program
    {
        static void Main(string[] args)
        {
            PrintNameProperty(typeof(ClassName));
            PrintNameProperty(typeof(ChildName));
            PrintNameProperty(NameILGenerator.ILType());
            Console.Read();
        }

        public static void PrintNameProperty(Type nameType)
        {
            var props = nameType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach(var prop in props)
            {
                Console.WriteLine("{0}.{1} IsPublic:{2}",nameType.Name, prop.Name, prop.GetMethod.IsPublic);
            }
        }
    }
}

I try to add MethodAttributes.NewSlot it did not work. When I change MethodAttributes.Family to MethodAttributes.Public there is a AmbiguousMatchException.

I searched for this Overriding property definitions with Reflection.Emit and change ILType to not define the property but only the get and set functions

        public static Type ILType()
        {
            AssemblyName aname = new AssemblyName("MyAssembly");
            AppDomain currentDomain = AppDomain.CurrentDomain; // Thread.GetDomain();
            AssemblyBuilder asmbuilder = currentDomain.DefineDynamicAssembly(aname,
                                       AssemblyBuilderAccess.Run);
            ModuleBuilder mbuilder = asmbuilder.DefineDynamicModule("EmitMethods");
            TypeBuilder tbuilder = mbuilder.DefineType("ILName", TypeAttributes.Public,typeof(ClassName)); 
            FieldBuilder fName = tbuilder.DefineField("_name", typeof(System.String), FieldAttributes.Private);
            //PropertyBuilder pName = tbuilder.DefineProperty("Name", PropertyAttributes.HasDefault, typeof(System.String), null);
            //Getter
            MethodBuilder mNameGet = tbuilder.DefineMethod("get_Name", MethodAttributes.Family | 
                MethodAttributes.SpecialName | 
                MethodAttributes.HideBySig, 
                typeof(System.String), 
                Type.EmptyTypes);
            ILGenerator nameGetIL = mNameGet.GetILGenerator();
            nameGetIL.Emit(OpCodes.Ldarg_0);
            nameGetIL.Emit(OpCodes.Ldfld, fName);
            nameGetIL.Emit(OpCodes.Ret);

            //Setter
            MethodBuilder mNameSet = tbuilder.DefineMethod("set_Name", MethodAttributes.Family | 
                MethodAttributes.SpecialName | 
                MethodAttributes.HideBySig, 
                null, 
                new Type[] { typeof(System.String) });

            ILGenerator nameSetIL = mNameSet.GetILGenerator();

            nameSetIL.Emit(OpCodes.Ldarg_0);
            nameSetIL.Emit(OpCodes.Ldarg_1);
            nameSetIL.Emit(OpCodes.Stfld, fName);
            nameSetIL.Emit(OpCodes.Ret);

            //pName.SetGetMethod(mNameGet);
            //pName.SetSetMethod(mNameSet);
            return tbuilder.CreateType();
        }

it shows one property but the property is Public. So how can i set the property to protected?

Fixed
  • Get method return type of String

Solution

  • You should use CallingConventions.HasThis when you define your property :

    tbuilder.DefineProperty("Name", 
                            PropertyAttributes.HasDefault, 
                            CallingConventions.HasThis,
                            typeof(System.String), 
                            null);
    

    You can have more information here : HasThis & ExplicitThis calling conventions

    By the way, it helps to call asmBuilder.Save("xxx.dll") after you create the type. It allows you to get the assembly file and decompile it using ILSpy. You will have to use AssemblyBuilderAccess.RunAndSave when you call DefineDynamicAssembly and specify a fileName when calling DefineDynamicModule.

    There is also one mistake in your code, your getter return an Int32 whereas the property is of type String