I understand that non-virtual methods are statically bound, which means, as far as I understand, that its known at compile-time itself as to which method will be invoked on which object. This decision is taken based on the static type of the object(s). What confuses me is interfaces (rather than class) and static binding.
Consider this code,
public interface IA
{
void f();
}
public class A : IA
{
public void f() { Console.WriteLine("A.f()"); }
}
public class B : A
{
public new void f() { Console.WriteLine("B.f()"); }
}
B b = new B();
b.f(); //calls B.f() //Line 1
IA ia = b as IA;
ia.f(); //calls A.f() //Line 2
Demo code: http://ideone.com/JOVmi
I understand the Line 1. The compiler can know that b.f() will invoke B.f() because it knows the static type of b which is B.
But how does the compiler decide at compile-time itself that ia.f() will call A.f()? What is the static type of object ia? Is it not IA? But then that is an interface, and doesn't have any definition of f(). Then how come it works?
To make the case more puzzling, lets consider this static method:
static void g(IA ia)
{
ia.f(); //What will it call? There can be too many classes implementing IA!
}
As the comment says, there can be too many classes that implement the interface IA, then how can the compile statically decide which method ia.f() would call? I mean, say if I've a class defined as:
public class C : A, IA
{
public new void f() { Console.WriteLine("C.f()"); }
}
As you see, C, unlike B, implements IA in addition to deriving from A. That means, we've a different behavior here:
g(new B()); //inside g(): ia.f() calls A.f() as before!
g(new C()); //inside g(): ia.f() doesn't calls A.f(), rather it calls C.f()
Demo code : http://ideone.com/awCor
How would I understand all these variations, especially how interfaces and static binding together work?
And few more (ideone):
C c = new C();
c.f(); //calls C.f()
IA ia = c as IA;
ia.f(); //calls C.f()
A a = c as A;
a.f(); //doesn't call C.f() - instead calls A.f()
IA iaa = a as IA;
iaa.f(); //calls C.f() - not A.f()
Please help me understanding all these, and how static binding is done by the C# compiler.
It doesn't. It knows that
ia.f()will be callingIA.f()on the object instance contained inia. It emits this call opcode and lets the runtime figure it out when the call is executed.Here is the IL that will be emitted for the bottom half of your example code:
Note that
callvirtis used in both cases. This is used because the runtime is able to figure out on its own when the target method is non-virtual. (Additionally,callvirtperforms an implicit null check on thethisargument, whilecalldoes not.)This IL dump should answer all of your other questions. In short: the compiler doesn't even attempt to resolve the final method call. That's a job for the runtime.