Reputation: 141
In the following sample class "SomeClass" does not implement "ISomeInterface". Why can't I implement this by passing a more derived interface which does implement the base requirement. Whatever instance would be passed it would still implement the base, am I missing something?
namespace Test
{
public interface IBaseInterface
{
void DoBaseStuff();
}
public interface IChildInterface : IBaseInterface
{
void DoChildStuff();
}
public interface ISomeInterface
{
void DoSomething(IBaseInterface baseInterface);
}
public class SomeClass : ISomeInterface
{
public void DoSomething(IChildInterface baseInterface)
{
}
}
}
Upvotes: 2
Views: 2720
Reputation: 10781
If you could do that, then you could do this:
IAnimal cat = new Cat();
IAnimalTrainer dogTrainer = new DogTrainer();
dogTrainer.Train(cat);
An IAnimalTrainer
can train any IAnimal
. But a DogTrainer
can only train Dog
s. Thus it's illegal for DogTrainer
to implement the IAnimalTrainer
interface.
Upvotes: 0
Reputation: 130
You should change some interface to use some type which implements IBaseInterface, then change the method signatures to use whichever child your SomeClass wants.
public interface ISomeInterface<TSomeChild> where TSomeChild : IBaseInterface
{
void DoSomething(TSomeChild baseInterface);
}
public class SomeClass : ISomeInterface<IChildInterface>
{
public void DoSomething(IChildInterface baseInterface)
{
}
}
Upvotes: 0
Reputation: 5953
This restriction exists because the ISomeInterface
expects that any IBaseInterface
will satisfy the contract. That is, if you have the following:
public interface IBase {}
public interface IChildA : IBase {}
public interface IChildB : IBase {}
And an interface that expects IBase
:
public interface IFoo { void Bar(IBase val); }
Then restricting this in a derived class as you would like:
public class Foo : IFoo { public void Bar(IChildA val) {} }
Would create the following problem:
IChildB something = new ChildB();
IFoo something = new Foo();
something.Bar(something); // This is an invalid call
As such, you're not implementing the contract you said you would.
In this situation, you have two simple options:
Adjust IFoo
to be generic, and accept a T
that is a derivation of IBase
:
public interface IFoo<T> where T : IBase { void Bar(T val); }
public class Foo : IFoo<IChildA> { public void Bar(IChildA val) {} }
Of course, this means that Foo
can no longer accept any IBase
(including IChildB
).
Adjust Foo
to implement IFoo
, with an additional utility method for void Bar(IChildA val)
:
public class Foo : IFoo
{
public void Bar(IBase val) {}
public void Bar(IChildA val) {}
}
This has an interesting side-effect: whenever you call ((IFoo)foo).Bar
it will expect IBase
, and when you call foo.Bar
it will expect IChildA
or IBase
. This means it satisfies the contract, while also having your derived-interface-specific method. If you want to "hide" the Bar(IBase)
method more, you could implement IFoo
explicitly:
void IFoo.Bar(IBase val) { }
This creates even more inconsistent behavior in your code, as now ((IFoo)foo).Bar
is completely different from foo.Bar
, but I leave the decision up to you.
This means, with the second version in this section, that foo.Bar(new ChildB());
is now invalid, as IChildB
is not an IChildA
.
Why can't I implement this by passing a more derived interface which does implement the base requirement. Whatever instance would be passed it would still implement the base, am I missing something?
This is not allowed because of the reasoning I mentioned above, IFoo.Bar
expects any IBase
, whereas you want to further constrain the type to IChildA
, which is not a super-interface of IBase
, and even if it were it would not be allowed because it violates the interface implementation, though you could more easily define a second method at that point that does what you want.
Keep in mind that when you implement an interface
, you subscribe to a contract, and C# will not let you violate that contract.
Upvotes: 4
Reputation: 37281
From MSDN:
When a class or struct implements an interface, the class or struct must provide an implementation for all of the members that the interface defines
This method in the derived
void DoSomething(IChildInterface baseInterface)
Does not have the same signature as the one in the interface:
void DoSomething(IBaseInterface baseInterface)
IChildInterface
and IBaseInterface
are not the same types. Therefore your derived class does not implement all methods of the interface and you get the compilation error.
For a possible the logic behind having this as a restriction instead of the compiler understanding the inheritance see Liskov's substitution principle as in SLakes answer
Upvotes: 2
Reputation: 888047
This violates the Liskov substitution principle.
ISomeInterface
guarantees that the method can be called with any IBaseInterface
instance. Your implementation cannot limit that to only accept IChildInterface
interfaces.
Upvotes: 3