Daniel
Daniel

Reputation: 31599

Using reflection to override virtual method tables in C#

Is there a way to change the virtual methods tables in C#? like change where a virtual method is pointing?

class A
{
    public virtual void B()
    {
        Console.WriteLine("B");
    }
}
class Program
{
    public static void MyB(A a)
    {
        Console.WriteLine("MyB");
    }
    public static void Main(string[] Args)
    {
        A a = new A();
        // Do some reflection voodoo to change the virtual methods table here to make B point to MyB
        a.B(); // Will print MyB
    }
}

Upvotes: 4

Views: 2941

Answers (4)

Marino Šimić
Marino Šimić

Reputation: 7342

You can make virtual B call a default delegate you choose. The following will allow inheritance and overriding: (actually overriding of already overriden :D)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class A
{
    public delegate void delegateB();
    public delegateB _B;

    public A() {
      _B = B;
    }
    public void Override(delegateB newB)
    {
        _B = newB;
    }

    public virtual void B()
    {
        if (_B != null && _B != this.B) {
            Console.WriteLine("OVERRIDEN B IN A");
            _B();
        }
        else {
        Console.WriteLine("VIRTUAL B IN A");
        }
    }
}

    class cB : A {
        public override void B() {
            if (base._B != null && base._B != this.B)
            {
                Console.WriteLine("OVERRIDEN B IN B");
                _B();
            }
            else
            {
                Console.WriteLine("IN B");
            }

        }
    }

class Program
{
    class Overrider {
       public void MyB()
       {
           Console.WriteLine("MyB");
       }
    }

    public static void Main(string[] Args)
    {
        A a = new A();
        a.B();
        Overrider ovr = new Overrider();
        a.Override(ovr.MyB);
        a.B(); // Will print MyB
        cB b = new cB();
        b.B();
        b.Override(ovr.MyB);
        b.B();
    }
}
}

Output:

VIRTUAL B IN A
OVERRIDEN B IN A
MyB
IN B
OVERRIDEN B IN B
MyB

You can even call base.B(); in cB.B() which will effectively call the overriden delegate but will give this output:

VIRTUAL B IN A
OVERRIDEN B IN A
MyB
IN B
OVERRIDEN B IN B
OVERRIDEN B IN A
MyB

I suggest you to take some similar approach or a design pattern approach and do not even think of reflection Emit. You code will be unusable in high performance applications. Delegates are fast, reflection is SLOW.

Upvotes: 1

corlettk
corlettk

Reputation: 13574

Dani,

What's your ACTUAL problem? Paint me a picture of where-abouts "breaking" an existing, tested class definition "on the fly" would be desirable. Forgive my scepticism... but I have plenty of scripting experience, so I'll take code that doesn't generate itself as it's running any day. I even (sort of) hate IOC for just that reason.

However, If you want to create an overriding (or implementing) class-definitions "on the fly" then I suppose you'd use byte-code-generation (atleast that's what you'd do in Java)... Here's a forum thread on that topic: http://bellyphant.com/2006/11/csharp_bytecode_generation

You could also generate source-code, and compiling it on the fly. Here's a cute free little utils class to compile C# on the fly: http://www.agilekiwi.com/on_the_fly.htm

Good luck with it anyway... It's an interesting question.

Cheers. Keith.

Upvotes: 1

Paolo Falabella
Paolo Falabella

Reputation: 25874

Take a look at LinFu.

On Linfu's author's Blog there's an example of using LinFu.AOP to intercept and change calls even to methods of classes that you don't control directly.

Upvotes: 4

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391614

You cannot change the types with reflection, you can only reflect over the existing types.

You can, however, build new types using Reflection.Emit and related classes, but short of recompiling your assembly, a.B(); in your example will always call A.B().

Upvotes: 2

Related Questions