- Part 1 - A Quirk With Implicit vs Explicit Interfaces (this post)
- Part 2 - What is an OpCode?
- Part 3 - Conditionals and Loops
The other day I got to work and the first thing I did was open an IL disassembler and got to town reading the IL of some code I was having a problem with.
That’s a pretty normal start to most .NET developers day right? Right…?
It all came about as I’m doing some exploration of Durable Entities which is part of the Durable Functions v2 preview that was announced at Build. I was using the new strongly-typed support that appeared in the beta-2 release, and allows you to write entities like this:
|
|
And then we can invoke it in a strongly-typed manner, rather than using magic strings:
|
|
This gives some nice type-safety to the way that you work with your entities.
Naturally though, I wasn’t writing this in C#, I was using F#, which means the code looks more like this:
|
|
And the invocation is:
|
|
But it kept throwing a highly cryptic error within the Durable Functions framework that the call to the method Add
failed, but it wouldn’t tell me why. After a bunch of debugging into the source of Durable Functions I found the cause of the failure, the Add
method wasn’t being found on the Counter
instance. But the C# code worked just fine, so what gives?
Well it turns out that in F# when you implement an interface it’s implemented explicitly, whereas in C# you can implement an interface implicitly or explicitly.
Interface Implementations, Implicit vs Explicit
Before really understanding the problem we need to understand a bit about how interface implementations work. Let’s do it in C# since it supports both types and we’ll start with an implicit implementation. This is what you’re most likely using when you’re working with interfaces, and it looks like this:
|
|
When you do an implicit interface implementation you are adding public
non-static methods to the class, and they have to be public
non-static (see here). What this means is that the class can be thought of as itself (Foo
) or its interface(s) (IFoo
) and the members provided by that interface are part of the class, they are implicitly there.
Ok, so what’s an explicit interface implementation look like?
|
|
The difference this time is that in our class
we have a non-public
Bar
function that is prefixed with the interface name (IFoo
). Since the member is non-public
we have to be explicit that the type is the interface if we want to access the members that are provided by the interface. In the docs there are a few scenarios of why you’d want to use explicit interface implementations over implicit, and it mainly comes down to how to handle multiple interfaces on a single type.
Since F# only supports explicit interface implementations you only think of types as their interface, which is how I tend to think of types-implementing-interfaces in C# anyway, so what’s the big deal?
Not All IL is Generated the Same
Back to the problem that I’d discovered, it was telling me that the type I was providing, Counter
, didn’t have a member Add
that could be accessed, and now that we understand how the members of an explicit interface are defined, that actually makes sense, there is no member Add
on Counter
, its name is actually ICounter.Add
, because it’s only part of the interface implementation.
Here’s the IL generated:
.method private final hidebysig newslot virtual
instance void UserQuery.ICounter.Add (
int32 count
) cil managed
{
.override method instance void UserQuery/ICounter::Add(int32)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: call instance int32 UserQuery/Counter::get_Count()
IL_0007: ldarg.1
IL_0008: add
IL_0009: call instance void UserQuery/Counter::set_Count(int32)
IL_000e: nop
IL_000f: ret
}
Compare that to the implicit interface implementation:
.method public final hidebysig newslot virtual
instance void Add (
int32 count
) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: call instance int32 UserQuery/Counter2::get_Count()
IL_0007: ldarg.1
IL_0008: add
IL_0009: call instance void UserQuery/Counter2::set_Count(int32)
IL_000e: nop
IL_000f: ret
}
Notice the difference between .method
definitions, our explicit is private
while implicit is public
and the name of the explicit is UserQuery.ICounter.Add
(Namespace.InterfaceName.MemberName
) compared to Add
for implicit.
Why Does It Matter?
Ok, it’s all very interesting learning about the differences in the IL generated by the compiler, but why is this important to know?
Well, it turns out that you need to understand this difference if you’re doing Reflection. Let’s say you have a type and you want to get a method of that type by its name. To do that you’d write code like this:
|
|
But this will fail if the method name you’re looking for is provided by an explicitly implemented interface!
Try out this code on try.dot.net:
|
|
The output will be:
|
|
Meaning that our call to GetMethod
and just providing Bar
will return null
since this is no method on that type with that name!
Conclusion
This was a really fun problem to try and solve, it’s been a long time since I dived deep into .NET internals and it’s quite interesting to learn the difference in the way interface implementations are handled.
There’s an open bug on Durable Functions to work out a way to resolve this and it turned out that I caught a few people with this one!