4

I ran into a problem today trying to override an implementation of an interface method which had not been declared virtual.

In this case I'm not able to change the interface or the base implementation and have to try something else, but I was wondering if there was a way to force a class to implement an interface using virtual methods.

Example:

interface IBuilder<T>
{
    // Already implicitly virtual
    /*virtual*/ T Build();
}

// This is a class written by someone else
class SimpleBuilder: IBuilder<SomeObject>
{
    // I would like to force this to be virtual
    public SomeObject Build() { return new SomeObject(); }
}

// This is the class I am writing.
class CustomBuilder: SimpleBuilder
{
    public /*override*/ SomeObject Build()
    {
        var obj = base.Build();
        obj.ModifyInSomeWay();
        return obj;
    }
}

Edit: CustomBuilder is intended to be used with MEF so I am deriving from SimpleBuilder in order for MEF to resolve the correct type. I've tried explicitly implementing the interface and not deriving from SimpleBuilder at all but then MEF doesn't pick up the right type.

The interface and base class in question are in a shared module created by another developer so it looks like I will have to get them to change their base class anyway.

andrews_nz
  • 175
  • 1
  • 9
  • You can explicitly implement the interface on the derived class, but that will only affect calls made through the interface, not the ones made through the derived class' own type (nor through its base class). – harold Jul 03 '12 at 10:46
  • 1
    Can you declare the `SimpleBuilder` and the `Build()` method as abstract? – CodeCaster Jul 03 '12 at 10:47
  • I'm assuming that someone else is the author of SimpleBuilder and I can't easily change it. I would like the interface to force that method to be declared virtual so when I write my custom version later on I can override it. – andrews_nz Jul 03 '12 at 10:51
  • possible duplicate of [Is it possible to override a non-virtual method?](http://stackoverflow.com/questions/1853896/is-it-possible-to-override-a-non-virtual-method) – Chris Gessler Jul 03 '12 at 10:57

5 Answers5

4

No there isn't, in your example you're implicitly implementing the interface. If your class is meant to be subclassed and a method overridden it is up to the developer to ensure that the method is marked as virtual.

There is no way to force implementations of interface methods to be overrideable in code, only conventions within a development team could ensure this (eg all interfaces should be explicitly implemented, all interface implementations should be marked as virtual etc).

In this specific example you could insert an abstract class into the hierarchy:

abstract class VirtualBuilder<T> : IBuilder<T>
{
  abstract T Build();
}

But this isn't suitable in the generic case, since you lose having the benefits of interfaces and 'force' all concrete classes to only implement one interface.

Rich O'Kelly
  • 41,274
  • 9
  • 83
  • 114
2

You can't force it to be virtual but you can use the new keyword; it's called Method Hiding.

// This is the class I am writing. 
class CustomBuilder: SimpleBuilder, IBuilder<T> 
{ 
    public new SomeObject Build() 
    { 
        var obj = base.Build(); 
        obj.ModifyInSomeWay(); 
        return obj; 
    } 
} 
Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
  • 1
    This approach is problematic. – leppie Jul 03 '12 at 10:45
  • @leppie - agreed. I don't use it very often, but in this case, it might be necessary – Chris Gessler Jul 03 '12 at 10:46
  • 1
    The `new` won't work in this case because the code which is using my new class is accessing it via the interface. – andrews_nz Jul 03 '12 at 10:47
  • @andrews_nz - You say "via the interface"?? Then it will work, just implement the interface and use the new key word. – Chris Gessler Jul 03 '12 at 10:58
  • @andrews_nz - This same approach was suggested here: http://weblogs.sqlteam.com/mladenp/archive/2007/04/09/60168.aspx – Chris Gessler Jul 03 '12 at 11:02
  • @ChrisGessler this works if I implement the interface directly. The custom class needs to be picked up as the most derived type by MEF too, and having the interface there seems to prevent that. My intention is to ask for a change to the base class so I can create my override. – andrews_nz Jul 03 '12 at 11:11
  • @andrews_nz - I agree. And your BEST option is to get the base class changed – Chris Gessler Jul 03 '12 at 11:20
2

Not directly, no.

You could "force" it by providing your own base class and requiring that users derive from it:

abstract class BuilderBase<T> : IBuilder<T>
{
    public abstract T Build();
}

But this has serious problems too becase

  1. it does not immediately stop anyone from implementing the interface directly (although you could e.g. require incoming parameters to be BuilderBase<T> instead of simply IBuilder<T>, or even make IBuilder<T> internal and hide it in another assembly)
  2. it forces implementers of the interface to give up their choice for the single base class, for which they might hate you
Jon
  • 428,835
  • 81
  • 738
  • 806
0

It is not possible to specify how an interface method will be implemented.

However, you have two ways of reimplementing the interface methods on the subclass even if the base implementation is not virtual:

  • hide the base implementation using the new method qualifier, or
  • explicit interface implementation like this:

    T IBuilder.Build() { var obj = Build(); obj.ModifyInSomeWay(); return obj; }

Either of these approaches will ensure that your method will be called only if your class is accessed via the interface, but not if the call uses a base class reference.

So I would say that this base class is not ready for extension through inheritance.

Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
0

You can't do this because an Inteface just describes how a class should look like and says nothing about the implementation itself.

In other words, if you use a class that implements an interface which defines a method Foo() you can be sure, the class has this method, regardless of the implementation.

What you can do:

public interface IBuilder<T>
{
    T Build();
}

public abstract class BaseBuilder<T> : IBuilder<T>
{
    public abstract T Build();
}

public class CustomBuilder : BaseBuilder<CustomBuilder>
{
    public override CustomBuilder Build()
    {
        throw new NotImplementedException();
    }
} 
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189