Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface inheritance with an internal base

I was wondering if there's a way to accomplish the following:

In my project, I have defined an interface, let's say IFruit. This interface has a public method GetName(). I also declare an interface IApple which implements IFruit and exposes some other method like GetAppleType() or something. There are more fruits like IBanana, ICherry, whatever.

Now on the outside, I want only to be able to use the actual fruit implementations and not IFruit itself. But I cannot declare the IFruit interface as private or internal, since the inherited interfaces will then say "cannot implement because the base class is less accessible".

I know this is possible with abstract implementations, but that's not an option in this case: I really need to use interfaces. Is there such an option?

Update I guess my example need some clarification :) I use MEF to load interface implementations. The loaded collections are based upon IApple, IBanana, ICherry, etc. But IFruit itself is useless, I can't use classes based on only that interface. So I was looking for a way to prevent other developers from implementing solely IFruit, thinking that their class will be loaded (which it won't). So basically, it comes down to:


internal interface IFruit
{
  public string GetName();
}

public interface IApple : IFruit { public decimal GetDiameter(); }

public interface IBanana : IFruit { public decimal GetLenght(); }

But that won't compile due to the less accessible base interface.

like image 555
Jasper Avatar asked May 29 '12 08:05

Jasper


2 Answers

One way that you can guarantee this doesn't happen unintentionally is to make IFruit internal to your assembly and then use some adaptor to wrap the type appropriately:

public interface IApple { string GetName(); }
public interface IBanana { string GetName(); }

internal interface IFruit { string GetName(); }

class FruitAdaptor: IFruit
{
    public FruitAdaptor(string name) { this.name = name; }
    private string name;
    public string GetName() { return name; }
}

// convenience methods for fruit:
static class IFruitExtensions
{
    public static IFruit AsFruit(this IBanana banana)
    {
        return new FruitAdaptor(banana.GetName());
    }

    public static IFruit AsFruit(this IApple apple)
    {
        return new FruitAdaptor(apple.GetName());
    }
}

Then:

MethodThatNeedsFruit(banana.AsFruit());

You could also easily extend this to lazily call GetName on the adapted object, if the name could change over time.


Another option could be to have a DEBUG-only check that does load all IFruit implementers, and then throws an exception if one of them doesn't actually implement IBanana/IApple. Since it sounds like these classes are for internal use inside your company, this should stop anyone from accidentally implementing the wrong thing.

like image 186
porges Avatar answered Nov 14 '22 20:11

porges


It isn't really possible to do what you're trying, but you can put people off using the IFruit interface with an [Obsolete] attribute, with message to say why.

On your IBanana, IApple, ... interfaces, disable the obsolete warning from appearing.

[Obsolete]
public interface IFruit {
    ...
}

#pragma warning disable 612
public interface IBanana : IFruit {
    ...
}
#pragma warning restore 612
like image 27
Mark H Avatar answered Nov 14 '22 21:11

Mark H