7

I have a class that is generated by some tool, therefore I can't change it. The generated class is very simple (no interface, no virtual methods):

class GeneratedFoo
{
  public void Write(string p) { /* do something */ }
}

In the C# project, we want to provide a way so that we can plug in a different implementation of MyFoo. So I'm thinking to make MyFoo derived from GeneratedFoo

class MyFoo : GeneratedFoo
{
  public new void Write(string p) { /* do different things */ }
}

Then I have a CreateFoo method that will either return an instance of GeneratedFoo or MyFoo class. However it always calls the method in GeneratedFoo.

GeneratedFoo foo = CreateFoo(); // if this returns MyFoo,
foo.Write("1"); // it stills calls GeneratedFoo.Write

This is expceted since it is not a virtual method. But I'm wondering if there is a way (a hack maybe) to make it call the derived method.

Thanks,
Ian

Hengyi
  • 733
  • 11
  • 20
  • "public new void" ? Thats a usage of "new" I've never seen before. Is that even valid? – abelenky Aug 14 '09 at 00:05
  • 'new' here indicates, that you have explicitly 'covered' method from base class, without overriding it (as it's not virtual) - without it you will actually get warning from compiler. – Marcin Deptuła Aug 14 '09 at 00:18
  • To be more specific - when you give your method in derived class the same name as in base, and it's not override method, compilers gives you a warning, that you your method hides inherited member (because you probably did not wanted to do this), but if you are sure about it, you tell that to the compiler using 'new' keyword. – Marcin Deptuła Aug 14 '09 at 00:21
  • I'm always thrilled when keywords are overloaded to have multiple meanings depending on context. Thanks for setting me straight. – abelenky Aug 14 '09 at 21:16

7 Answers7

22

Adam gave you an answer (correct one). Now it's time for hack you were asking for :)


class BaseFoo
{
    public void Write() { Console.WriteLine("Hello simple world"); }
}

class DerFoo : BaseFoo
{
    public void Write() { Console.WriteLine("Hello complicated world"); }
}

public static void Main()
{
    BaseFoo bs = new DerFoo();
    bs.Write();

    bs.GetType().GetMethod("Write").Invoke(bs, null);
}

Prints out:

Hello simple world
Hello complicated world
Marcin Deptuła
  • 11,789
  • 2
  • 33
  • 41
  • There's no need for unsafe there – Chris Chilvers Aug 13 '09 at 23:57
  • Aye, this unsafe is here because I have few "test" projects that I open in VS when I need to cook some really strange code, so Main is unsafe by default, and I also have lots of strange usings there hehe. – Marcin Deptuła Aug 13 '09 at 23:59
  • Anyway, unsafe removed for clarity (as it's just obsolete), thanks for commenting it out. – Marcin Deptuła Aug 14 '09 at 00:01
  • Using reflection for this is slooooooow and completely unnecessary. Plus it is best to avoid relying on magic strings whenever possible. I recommend using Tullo's extension method (on this page) to get the same result. – Michael Graczyk Jun 13 '12 at 01:38
  • 1
    @MichaelGraczyk You are right about speed concerns of course. However, my solution differs from Tullo's, because it will always call the "right" method. Where "right" is underlying's type implementation, even, if you have chain of inheritance (so you would have to check each base type in chain and find the most base one). Also, if you need speed, because you will be calling this method often, you can create delegate from GetMethod("") once you get valid method. Nevertheless, I agree, that it was a hack, mostly to show that you have possibility of doing such a thing. – Marcin Deptuła Jun 13 '12 at 08:20
  • It is true that yours is more "right" with respect to expected dispatch semantics. I sort of assumed he was only going to derive from the generate class one level down. I +1'ed you either way. – Michael Graczyk Jun 13 '12 at 11:01
  • Awesome - thanks - I am using this in Windows Forms to iterate through a collection of custom controls that are added to a table dynamically. I have 28 different control types and the other option would have been to iterate though all the different types. This saved me at least 150 lines of code... Plus if I add more control types later they will just work. – Anthony Nichols May 31 '13 at 20:20
6

Without being able to make the method virtual, no. A non-virtual method is statically linked at compile time and can't be changed.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
6

Write an extension method that safecasts to your derived type, and calls the method against that reference instead.

public static class Extensions
{
  public static void WriteFoo(this GeneratedFoo foo, string p)
  {
     MyFoo derived = foo as MyFoo;
     if (derived != null)
     {
        derived.Write(p);
     }
     else
     {
        foo.Write(p);
     }
  }
}

then call it with

GeneratedFoo unknownFoo;
unknownFoo.WriteFoo("win");

NB: this is a dirty hack. A clearer solution would be to ask yourself why you need to use the new modifier in the first place. Overloading the meaning of Write(p) will make your code confusing for maintainers. You could just as easily declare it public void WriteToTarget() or something much more specific to avoid that confusion in the first place.

Tullo_x86
  • 2,573
  • 25
  • 27
3

Using "new" on a method is not overriding, its hiding the original method. This is completely different to polymorphism and should be avoided, its only purpose is for covariance/contra-variance of methods that perform an identical function (eg, public Foo Clone(); and public new Bar Clone();). The other purpose is for legacy code when an identically named method is added to a base class that you have no control over and cannot at this time change your method's name.

Though the two methods share the same name they are completely separate methods, it occupies a different slot in the class's method table, where as overriding replaces the existing method in a class's method table.

My solution to this would be to use an interface such as IFoo, and either edit the generated class or use a proxy class that delegates all its method calls to an instance of GeneratedFoo to implement IFoo.


public DelegatingFoo : IFoo
{
  private GeneratedFoo foo;

  public DelegatingFoo(GeneratedFoo foo) { this.foo = foo; }
  public void Write(string p) { foo.Write(p); }
}
Chris Chilvers
  • 6,429
  • 3
  • 32
  • 53
1

If the generated class has no virtual method then it is not possible. If you could modify the tool to generate the Write method as virtual then in MyFoo use the keyword override instead of new to qualify the Write method. A second option would be to write a script/macro to change the source code of GeneratedFoo after the tool has run to inject the string "virtual" before desired methods as part of your build process.

softveda
  • 10,858
  • 6
  • 42
  • 50
0

What about a custom FX cop rule added to your build system?

Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317
-1

An approach that you could use would be to actually define a few new classes all derived from GeneratedFoo; eg:

public class MyFooBase : GeneratedFoo
{
    public virtual void MyMethod() { ... }
}

public class MyFoo1 : MyFooBase { ... }

public class MyFoo2 : MyFooBase { ... }

This would allow you to essentially add virtual methods to your generated class.

Chris Shaffer
  • 32,199
  • 5
  • 49
  • 61
  • But not invoke non-virtual method from generated foo as it would be virtual. – Marcin Deptuła Aug 13 '09 at 23:56
  • But you could write a new method that WAS virtual and called the nonvirtual in the derived class that you wanted to have that behavior and did not call the nonvirtual in the derived class that should not have that behavior. – Chris Shaffer Aug 14 '09 at 01:06