Implementing interface at run-time: get_Value method not implemented
Asked Answered
M

3

15

I was trying to define a type at run-time that inherits from a known class and implements an interface.

public class ParentClass
{
}

public interface IImplementMe
{
    double Value{get;set}
}

Here's the code snippet that shows how I try to achieve my goal.

   public class ClassBuilder
   {
    public Type Build()
    {
        try
        {
            AssemblyName assemblyName = new AssemblyName("DataBuilderAssembly");
            AssemblyBuilder assemBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemBuilder.DefineDynamicModule("DataBuilderModule");
            TypeBuilder typeBuilder = moduleBuilder.DefineType("NewClass", TypeAttributes.Class, typeof(ParentClass));
            typeBuilder.AddInterfaceImplementation(typeof(IImplementMe));
            BuildProperty(typeBuilder, "Value", typeof(double));
            Type type = typeBuilder.CreateType();

            return type;
        }
        catch (Exception e)
        {
            return null;
        }

    }

    private void BuildProperty(TypeBuilder typeBuilder, string name, Type type)
    {
        FieldBuilder field = typeBuilder.DefineField("m" + name, type, FieldAttributes.Private);
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None, type, null);

        MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;

        MethodBuilder getter = typeBuilder.DefineMethod("get_" + name, getSetAttr, type, Type.EmptyTypes);

        ILGenerator getIL = getter.GetILGenerator();
        getIL.Emit(OpCodes.Ldarg_0);
        getIL.Emit(OpCodes.Ldfld, field);
        getIL.Emit(OpCodes.Ret);

        MethodBuilder setter = typeBuilder.DefineMethod("set_" + name, getSetAttr, null, new Type[] { type });

        ILGenerator setIL = setter.GetILGenerator();
        setIL.Emit(OpCodes.Ldarg_0);
        setIL.Emit(OpCodes.Ldarg_1);
        setIL.Emit(OpCodes.Stfld, field);
        setIL.Emit(OpCodes.Ret);


        propertyBuilder.SetGetMethod(getter);
        propertyBuilder.SetSetMethod(setter);
    }
}

For some reason I get an "get_Value method not implemented" exception on calling typeBuilder.CreateType(). So far unable to see the reason behind it.

Meissen answered 11/3, 2011 at 19:13 Comment(0)
S
12

Changing your line to:

MethodAttributes getSetAttr = MethodAttributes.Public | 
    MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;

Your code then worked for me. (The virtual was required.)

Stillborn answered 11/3, 2011 at 19:18 Comment(2)
Tried adding 'MethodAttributes.SpecialName', but that didn't help. :/Meissen
@L.E.O., I tried your code myself, and it worked fine when I added all the method attributes I mentioned (virtual seems to have been required.)Stillborn
S
14

The c# implicit interface implementation (by name match) is convenience only; when doing it by hand you need to associate each in the virtual-method table using typeBuilder.DefineMethodOverride, passing in the new (generated) method and the interface method to satisfy. This must done for each method of every interface you want to implement.

Salk answered 11/3, 2011 at 19:27 Comment(1)
That's something I'll be aware of. Thanks.Meissen
S
12

Changing your line to:

MethodAttributes getSetAttr = MethodAttributes.Public | 
    MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;

Your code then worked for me. (The virtual was required.)

Stillborn answered 11/3, 2011 at 19:18 Comment(2)
Tried adding 'MethodAttributes.SpecialName', but that didn't help. :/Meissen
@L.E.O., I tried your code myself, and it worked fine when I added all the method attributes I mentioned (virtual seems to have been required.)Stillborn
C
0

You need to mark it as virtual, and explicitly bind the implementation to the interface:

                // Associate the methods with the interface
                foreach (var interfaceType in typeBuilder.ImplementedInterfaces)
                {
                    var getMethod = interfaceType.GetMethod($"get_{fieldName}");
                    var setMethod = interfaceType.GetMethod($"set_{fieldName}");

                    if (getMethod != null)
                    {
                        typeBuilder.DefineMethodOverride(
                            getterBuilder,
                            getMethod);
                    }

                    if (setMethod != null)
                    {
                        typeBuilder.DefineMethodOverride(
                            setterBuilder,
                            setMethod);
                    }
                }
Collings answered 14/3, 2023 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.