Why isn't it possible to cast an instance of:
sealed class Foo
{
public void Go() { }
}
...to this interface:
interface IBar
{
void Go();
}
...even though Foo
has the signature of IBar
?
How can I turn an instance of Foo
into an IBar
? Assume I have no control over Foo
.
No, C# does not support duck-typing.
The OOP-approach to this problem is to use the Adapter Pattern.
You would do this:
class FooBarAdapter : IBar {
private readonly Foo foo;
public FooBarAdapter(Foo foo) {
this.foo = foo;
}
public void Go() {
this.foo.Go();
}
}
Whenever you have a Foo
but need an IBar
you wrap it on-demand:
public void ContrivedScenario() {
Foo foo = GetFooFromExternalDependency();
FooBarAdapter adapter = new FooBarAdapter( foo );
this.NeedsIBar( adapter );
}
public void NeedsIBar(IBar bar) { ... }
I note that if Foo
-to-IBar
conversion happens a lot, you can make use of implicit conversions, so you don't need to explicitly construct FooBarAdapter
instances, but it is debatable if this is good software engineering practice or not:
class FooBarAdapter : IBar {
// (same as above)
public static implicit operator FooBarAdapter(Foo foo) {
return new FooBarAdapter( foo );
}
}
That way you can do this:
public void ContrivedScenario() {
Foo foo = GetFooFromExternalDependency();
this.NeedsIBar( foo ); // the conversion from `Foo foo` to `FooBarAdapter` happens implicitly
}
One reason why C# doesn't support duck-typing is because just because a class's interface (in the OOP sense, not a literal interface
) shares the same identifiers as another, doesn't mean they're compatible. For example Process.Kill
(kills the process) and MegaDeathKillBot3000.Kill
(kills all humanity) probably shouldn't be used interchangeably... unless you really want to.
In addition to @Dai's answer, an alternative is using a mock framework, if you want to do your foo-bar cast in a test project.
var bar = Mock.Of<IBar>();
Mock.Get(bar).Setup(b => b.Go()).Callback(() => foo.Go());
MethodThatNeedsIBar(bar);
The framework creates a proxy of type IBar
to foo
, working like an adapter, but it's easier to setup and less code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With