Stijn Van Antwerpen
Stijn Van Antwerpen

Reputation: 1986

How to use an extension method to make an interface complete

Given an interface IFoo

interface IFoo {
    void Do();
    void Stuff();
}

Let assume there are (legacy) classes Foo1, Foo2, Foo3 all implementing IFoo.

Stuff can be done by using some methods of IFoo, or in case of the newer classes, by just using DoStuff(). Actually, one might look at it as if DoStuff() was "forgotten" on IFoo.

There are also newer Classes FooX (FooY, ...) implementing IFoo2, in additional those has a method DoStuff();

 interface IFoo2 : IFoo {
        void DoStuff();
   }

I need to accept IFoo objects, and be able to "Do Stuff" on it.

 //Let us assume foos = new IFoo[] {new Foo1(), new Foo2(), new Foo3(), new FooX()};

 void MyMethod(IFoo[] foos){
     foreach(foo in foos){
        //DoStuff is not defined in IFoo
        foo.DoStuff();
     }
 }

So, I thought to just define an extension method DoStuff() on IFoo for the legacy classes

public static DoStuff(this IFoo self){
    self.Do();
    self.Stuff();
}

Unfortunately, this extension method is always called, even for FooX.

I Could do something like

public static DoSomeStuff(this IFoo self){
    if(self is IFoo2) {
        (self as IFoo2).DoStuff()
    } else {
       self.Do();
       self.Stuff();
    }
}

void MyMethod(IFoo[] foos){
     foreach(foo in foos){            
        foo.DoSomeStuff();
     }
 }

However, the method MyMethod reside in a legacy project, currently not yet aware of IFoo2. Is it possible to find a solution without using IFoo2?

Upvotes: 1

Views: 120

Answers (4)

InBetween
InBetween

Reputation: 32780

IMHO FooX shouldn't be implementing IFoo to begin with and some reconsideration should be made of your current arquitecture.

That said, and not knowing exactly what limitations you are fighting against, could you send the IFooXs through a wrapper? Something like the following:

public class FooXWrapper<T>: IFoo where T: FooX
{
    readonly T foo;
    bool doCalled;

    public FooWrapper(T foo)
    {
        this.foo = foo;
    }

    public void Do()
    {
        doCalled = true;
    }

    public void Stuff()
    {
         if (!doCalled)
             throw new InvalidOperationException("Must call Do");

         foo.DoStuff();
    }
}

Its an ugly hack, but given the circumstances...

Upvotes: 0

martijn
martijn

Reputation: 1469

You can create an abstract subclass that implements the methods Do and Stuff for the classes that at the current time don't implement it.

public abstract class abstractFoo : IFoo 
{
    public virtual void Do() {}
    public virtual void Stuff(){}
}

If you can then inherit from this abstract class

public class Foo: IFoo 
{
    // interface implementation required
}

becomes:

public class Foo: abstractFoo
{
    // interface implementation NOT required
}

Upvotes: 0

Pawel Maga
Pawel Maga

Reputation: 5807

You shouldn't extend IFoo interface, like that. It's break Interface Segregation principle.

If these object represents exactly the same entity in your code you shouldn't use different interfaces for them. You might create extension method if you want extend functionality of classes which implements interface IFoo, but don't create second interface which represents the same contract. However if you want to change IFoo contract - refactor legacy objects (add missing implementation).

Upvotes: 2

Joey
Joey

Reputation: 354794

As long as your variable has the type IFoo, the extension method DoStuff will be called. The usual way of solving this is exactly what you propose in your last paragraph, or ensuring that you use IFoo2 instead of IFoo in places where you want the newer interface method to be called.

Upvotes: 0

Related Questions