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?

Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
Brian Reichle
  • 2,798
  • 2
  • 30
  • 34
  • 3
    Your code sample does not show that `SubClass` implements `IStuff`. Did you mean to write `Class SubClass : BaseClass, IStuff`? – Jim Mischel Jan 26 '11 at 07:31

2 Answers2

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);
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
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.

Community
  • 1
  • 1
kvb
  • 54,864
  • 2
  • 91
  • 133
  • 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 Reichle Jan 28 '11 at 02:09
  • @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). – kvb Jan 28 '11 at 18:30