4

I have a problem with using inherited interface. I will explain my problem on example below. Let's say I have Interface IFlyable:

public interface IFlyable
{
    IVerticalSpeed Speed { get; set; }
}

It contains IVerticalSpeed interface. I created another interface called ISpeed which inherits from IVerticalSpeed interface:

public interface ISpeed : IVerticalSpeed
{
    int MaxSpeed { get; set; }
}

In next step I created a class Fly which implement IFlyable interface:

public class Fly : IFlyable
{
    public IVerticalSpeed Speed { get; set; }
}

Everything is fine... but what if I wanted to replace IVerticalSpeed interface to ISpeed interface which inherits from IVerticalSpeed interface?

public class Fly : IFlyable
{
    public ISpeed Speed { get; set; }
}

I thought that everything should be fine because my ISpeed interface is IVertialSpeed interface + anything that ISpeed interface contatins. But that is not right. I get error which says: "Fly does not implement interface member IFlyable.Speed (...). Why?

Adam Stepniak
  • 815
  • 6
  • 21
  • 2
    you dont show IVerticalSpeed – Seabizkit Feb 28 '17 at 13:58
  • 1
    Possible duplicate of [Why can't C# interfaces contain fields?](http://stackoverflow.com/questions/2115114/why-cant-c-sharp-interfaces-contain-fields) – cokceken Feb 28 '17 at 14:00
  • @Seabizkit - No, but that shouldn't matter. `interface IVerticalSpeed {}` is enough. – H H Feb 28 '17 at 14:03
  • at Adam, no , more info helps. I cant not see if IVerticalSpeed has IVerticalSpeed as a property.... odd yes but so is the question, so asking people to help with limited info doesn't paint the whole picture. So the answer would be then that `Fly` no longer implements `IVerticalSpeed` as a property as denoted by 'IFlyable' – Seabizkit Feb 28 '17 at 14:13

5 Answers5

4

All answers show you possible solutions but none actually answer the important question here:

I thought that everything should be fine because my ISpeed interface is IVertialSpeed interface + anything that ISpeed interface contatins. But that is not right. I get error which says: "Fly does not implement interface member IFlyable.Speed (...). Why?

You've said it yourself. ISpeed is an IVerticalSpeed, but not all IVerticalSpeeds are ISpeeds so you are not satisfying the contract at all.

What would happen if your code was allowed and I wanted to do the following:

public interface IMyOtherSpeed: IVerticalSpeed { ... }
IFlyable myFly = new Fly();
IMyOtherSpeed mySpeed = new MyOtherSpeed();

myFly.Speed = mySpeed; //Runtime error, mySpeed is not an ISpeed?!?

You see the issue now? You'd be breaking the interface's contract, becuase your class only accepts ISpeed when it should accept any IVerticalSpeed.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • 1
    Another way to put the emphasis: the contract is broken by the `set`, the OP was reasoning about the `get` part. – H H Feb 28 '17 at 14:14
2

If you use generics you can do something like

public interface IFlyable<out T> : where T IVerticalSpeed
{ T Speed {get;set;} }

Then in your class you would do something like

public class Fly : IFlyable<SomeSpeedClass>
{
  public SomeSpeedClass Speed{get;set}
}
dcg
  • 4,187
  • 1
  • 18
  • 32
  • Do you think of an airplane as an airplane of vertical speed? Just because its possible to do with generics doesn't mean it should. This particular example seems like a bad fit for genercis, to be honest. – InBetween Feb 28 '17 at 14:28
  • @InBetween what would the best approach for this? Because the `Fly` class has to implement `IFlyable` interface but he wants an instance of `ISpeed` returned in the Speed property instead of an `IVerticalSpeed` instance. – dcg Feb 28 '17 at 14:39
  • Without context I wouldn't be able to answer what the best approach is. But its reasonable to expect that `IFlyable` will have similar properties for `HorizontalSpeed`, `HorizontalAcceleration`, `VerticalAcceleration`, etc. Are you going to create an `IFlyable` interface? And lets not even get started on how to deal with generic type variance... it all gets very messy very fast. Generics shouldn't be a solution just to make the type system work. – InBetween Feb 28 '17 at 14:43
1

Instead of

public class Fly : IFlyable
{
    public ISpeed Speed { get; set; }
}

You should match the IFlyable interface

public class Fly : IFlyable
{
    public IVerticalSpeed Speed { get; set; }
}

Interfaces are not inheritance, must be matched exactly.

Edit: Let me rephrase, interfaces that implementing other interfaces is not inheritance, hence the error. Interfaces must me matched exactly, that's is why it raises the error (ISpeed != IVerticalSpeed)

Cleptus
  • 3,446
  • 4
  • 28
  • 34
1

Because your property's signature must stay 100% the same. You can implement a new property, as a workaround. But it has it's limitations, too.

You can do this if your inheritance would work both ways. You already know, all ISpeeds are IVerticalSpeeds. But that's not the case the other way round. Not all IVerticalSpeeds are ISpeeds. So we can give your class another method, but it will face a problem at exactly that point:

  public interface IVerticalSpeed
  {
    int Value { get; set; }
  }

  public interface IFlyable
  {
    IVerticalSpeed Speed { get; set; }
  }

  public interface ISpeed : IVerticalSpeed
  {
    int MaxSpeed { get; set; }
  }

  public class Fly : IFlyable
  {
    public ISpeed Speed { get; set; }

    IVerticalSpeed IFlyable.Speed
    {
      get
      {
        return this.Speed;
      }
      set
      {
        // Wow, wait, you want to SET an IVerticalSpeed,
        // but not every IVerticalSpeed is an ISpeed... what now?
        // this.Speed = value;
      }
    }
  }

This is not a problem of code, this is a design problem. What do you want to do, if someone sets an IVerticalSpeed to your Fly? Because your interface says that's allowed. Maybe you don't want setters?

nvoigt
  • 75,013
  • 26
  • 93
  • 142
0

You haven't shown IVerticalSpeed, but if you have control of it consider

public interface IFlyable<out TSpeed>
    where TSpeed : IVerticalSpeed
{
    TSpeed Speed { get; set; }
}

then implemented in Fly like this

public class Fly : IFlyable<ISpeed>
{
    public ISpeed Speed { get; set; }
}
David Culp
  • 5,354
  • 3
  • 24
  • 32