12

I am attempting to generate a dynamic class implementing an interface, but where one or more of the members already exists in the base. I compiled the following code in C# and examined it in reflector to see what the C# compiler does.

class BaseClass
{
    public string Bob
    {
        get { return "Bob"; }
    }
}

interface IStuff
{
    string Bob { get; }
}

class SubClass : BaseClass, IStuff
{
}

Reflector does not show any implementation in SubClass.

.class private auto ansi beforefieldinit SubClass
    extends Enterprise.Services.OperationalActions.Business.Filters.BaseClass
    implements Enterprise.Services.OperationalActions.Business.Filters.IStuff
{
}

But if I do not emit the member explicitly, TypeBuilder.CreateType() throws an InvalidOperationException stating that the member does not have an implementation. So my question is, how do I tell TypeBuilder that an interface member should take it's implementation from the base?

1
  • 3
    Your code sample does not show that SubClass implements IStuff. Did you mean to write Class SubClass : BaseClass, IStuff? Commented Jan 26, 2011 at 7:31

2 Answers 2

10

It looks like with TypeBuilder you will have to add a private pass-thru, just to make it happy (below). You could also try using the IKVM builder - almost identical API, but it might not have this limitation.

using System;
using System.Reflection;
using System.Reflection.Emit;
public class BaseClass
{
    public string Bob
    {
        get { return "Bob"; }
    }
}

public interface IStuff
{
    string Bob { get; }
}
static class Program
{
    static void Main()
    {
        var name = new AssemblyName("foo");
        var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
        var mod = asm.DefineDynamicModule("foo");
        var parent = typeof(BaseClass);
        var type = mod.DefineType("SubClass", parent.Attributes, parent);
        type.AddInterfaceImplementation(typeof(IStuff));

        var bob_get = type.DefineMethod("bob_get", MethodAttributes.Virtual | MethodAttributes.Private,
            typeof(string), Type.EmptyTypes);
        var il = bob_get.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Callvirt, parent.GetProperty("Bob").GetGetMethod(), null);
        il.Emit(OpCodes.Ret);
        type.DefineMethodOverride(bob_get, typeof(IStuff).GetProperty("Bob").GetGetMethod());
        var final = type.CreateType();
        IStuff obj = (IStuff) Activator.CreateInstance(final);
        Console.WriteLine(obj.Bob);
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, it's unfortunate I need a wrapper property but this should still be sufficient for my needs.
6

The C# compiler actually emits different code for BaseType depending on whether your SubClass definition is in the same assembly or not. So if you just have this:

interface IStuff
{
    string Bob { get; }
}

public class BaseClass
{
    public string Bob
    {
        get { return "Bob"; }
    }
}

and then define SubClass in another C# project, then the compiler will actually emit an explicit interface implementation within it. This is because in this case, BaseClass.get_Bob will be defined as non-virtual, which means that it can't be used to satisfy the interface's contract.

See also Why are C# interface methods not declared abstract or virtual?, which explicitly discusses this oddity at the end of the answer.

2 Comments

I would have thought that the vtable record for the interface would simply point to the implementation by BaseClass. I'll have to go over the link when I get home this evening, looks like it could be interesting.
@Brian - I believe that only virtual methods appear in consistent slots within subclasses' method tables. So for the interface to point to a slot in BaseClass's method table which will also work for subclasses, the method must be virtual, which is why all interface method implementations in IL must be virtual (although the C# compiler doesn't require you to annotate them as such).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.