简体   繁体   中英

How to force call a C# derived method

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

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

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

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.

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); }
}

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.

添加到构建系统的自定义FX cop规则如何处理?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM