views:

514

answers:

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

+3  A: 

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
A: 

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.

Pratik
+7  A: 

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
Ravadre
There's no need for unsafe there
KeeperOfTheSoul
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.
Ravadre
Anyway, unsafe removed for clarity (as it's just obsolete), thanks for commenting it out.
Ravadre
A: 

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
But not invoke non-virtual method from generated foo as it would be virtual.
Ravadre
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
A: 

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); }
}
KeeperOfTheSoul
+2  A: 

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");
Tullo
A: 

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

Ian Ringrose