3

In an abstract class

public abstract class base
    {
        abstract public int Func(bool flag = true);
    }

I made a change in the derived version:

public class derived : base
    {
        public override int Func(bool flag = false){}
    }

Debugging shows the compiler uses true as default. I expected otherwise, why does it behave thus?

adrianm
  • 14,468
  • 5
  • 55
  • 102
JNF
  • 3,696
  • 3
  • 31
  • 64
  • I dont see where you derive it? You have just named it the same/ – David Pilkington Dec 08 '13 at 11:05
  • Optional parameters are assigned when calling the method. How do you expect the compiler to know which override is actually called at runtime? It uses the static type when assigning optional parameters – adrianm Dec 08 '13 at 11:08
  • @adrianm, thanks for the edit. I would assume, at least, for the compiler to detect a mismatch, if not take the derived. – JNF Dec 08 '13 at 13:09

2 Answers2

3

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.

BlackBear
  • 22,411
  • 10
  • 48
  • 86
  • In a short word I think `override` is used to override the **implementation**, not the **argument**, we can't override the argument, the arguments should always be the same to the base method. – King King Dec 09 '13 at 06:41
  • @KingKing yes, that's a good tl-dr ;) – BlackBear Dec 09 '13 at 07:23
1

I've created a sample that mimics your situation. Long story short: which default value is used depends on whether you access the instance by a variable of the type of the base class or the derived class. So the compiler detects the default value of the optional parameter based on the type of variable. This makes sense as you could store any instance of a derived class in the variable (polymorphism). It might not even be clear at compile time, which the type if instance is (e.g. if you configure your system to use a specific type). All the compiler knows is the type of the variable and that any instance that you put into it in a type-safe manner offers the same interface.

using System;

public abstract class MyBase
{
    public abstract bool GetValue(bool value = true);
}

public class MyDerived : MyBase
{
    public override bool GetValue(bool value = false)
    {
        return value;
    }
}

public class Test
{
    public static void Main()
    {
        var derived = new MyDerived();
        Console.WriteLine("Value = {0}", derived.GetValue());
        MyBase myBase = derived;
        Console.WriteLine("Value = {0}", myBase.GetValue());
    }
}

I assume that you call the method on a variable of the type of the base class which would explain the behavior.
Basically, this resembles the behavior if you don't override the method but shadow it. It is a situation you will want to avoid in almost all circumstances because the risk that the code will not behave in the way you expect it is relatively high.
Therefore I'd suggest to get rid of the optional parameters and make them required ones (and maybe create an overload without the parameter). Having to type less and being able to change the default value later is not worth the risk of having a unexpected behavior of your code. The behavior might be transparent to you now, but I doubt that it will be some months later or to coworkers of yours that have to maintain the code.

Markus
  • 20,838
  • 4
  • 31
  • 55
  • That's interesting. Do you have an idea why the compiler doesn't cry out? – JNF Dec 08 '13 at 13:15
  • The compiler seems to regard optional parameters on overridable methods as a valid construct (though it might be the safest option do forbid optional parameters on such methods). As it only knows the type of the variable and by that the default parameter on base level, it cannot detect whether there is a mismatch with a derived implementation that might possibly be the type of the instance that is stored in the variable. – Markus Dec 08 '13 at 13:44