I think this is particularly likely to confuse people who (like me) learned about virtual vs. non-virtual methods in C++, and about interfaces in Java, and later had to figure out how the two might interact in C#.
Method dispatch through an interface reference in Java is (comparatively) straightforward, because all methods in Java are virtual. So of course the instance runtime type decides what's invoked and that's that... in Java.
This does mean that certain more complex relationships between instance, method name, and implementation are not available in Java, which can be argued as "a good thing" or "a bad thing", depending on your point of view.
Regardless, this is one of the big differences between Java and C#, because not only are methods not required to be virtual in C#, they aren't by default. And without getting into implementation details, that leads to a key insight:
If a method isn't marked virtual
in a parent class, then you can't mark a corresponding (same name and signature) method in the child class as override
. And, separately, if a method in a child class isn't marked as override
, then it behaves as new
whether or not it had been virtual in the base class.
So... if a class implements an interface with a non-virtual
method, is virtual-like dispatch used when you want to invoke the method through an interface reference? It would seem like a reasonable way to do it. (Think of all the ways an interface reference could end up pointed at an instance of a particular class, that might not be knowable when the code using the interface reference is compiled. Virtual dispatch just makes sense here.) Still it's not the only way.
BUT:
It doesn't matter. Even if it is, in C# virtual
doesn't mean that a child-class implementation has to override the base-class method; it merely means that it can if declared to do so. And by not explicitly declaring the base-class method virtual
, you make it impossible to compile a child class with a method that declares itself an override of that method.
Update : The currently-top-rated answer claims that the key decision is made at compile time, and while an implementation could do that in the specific example, I don't believe it does - simply because that approach doesn't generalize.
public void myMethod(I i) {
i.f();
}
When compiling the above method, the compiler has absolutely no clue what actual implementation it should invoke. And when compiling some other unit of code with the line
myMethod(new A());
the compiler doesn't know that f()
specifically will need to be resolved. So the compiler, handling this second block, can set up information to be used by the output from compiling the first block, in deciding how to dispatch any given method.
But ultimately the decision to actually execute a given method implementation can't be made until runtime; whether that takes the form of virtual dispatch, or some reflection-based monstrosity, or whatever.
At the language level, that's all just implementation details. The specified behavior is what's important, and that is where the relationship between the virtual
and override
keywords becomes key.