This behavior is stated in the C# language specification at §7.5.1.1 (Corresponding Parameters, emphasis mine):
For each argument in an argument list there has to be a corresponding
parameter in the function member or delegate being invoked. The
parameter list used in the following is determined as follows:
- For virtual methods and indexers defined in classes, the parameter list is
picked from the most specific declaration or override of the function
member, starting with the static type of the receiver, and searching
through its base classes
During binding (the process of associating the method to call to an invocation) the compiler finds a list of arguments using the rules stated above then a set of candidate methods conforming to that argument list. Among these the best one is picked and bound to the invocation.
Take this code:
BaseClass b = new DerivedClass( );
b.Func( );
During binding the argument list used is the one declared in BaseClass.Func
(because that is the static type of b
) and the set of candidate methods is BaseClass.Func
and DerivedClass.Func
(they are said to be applicable, because both argument list correspond to the one chosen). Then DerivedClass.Func
is decided to be the best candidate, as expected, and therefore bound to the call using the parameter list of BaseClass.Func
, thus using flag = true
.
You can find more details in §7.5.3 (Overload resolution). Finally, in case you wonder, abstract methods are considered virtual, as noted in §10.6.6 (Abstract methods):
An abstract method declaration introduces a new virtual method but does not provide
an implementation of that method.