C# Optional Parameters on Overridden Methods

C# optional parameters on overridden methods

One thing worth noting here, is that the overridden version is called each time. Change the override to:

public override void MyMethod(string s = "bbb")
{
Console.Write("derived: ");
base.MyMethod(s);
}

And the output is:

derived: bbb
derived: aaa

A method in a class can do one or two of the following:

  1. It defines an interface for other code to call.
  2. It defines an implementation to execute when called.

It may not do both, as an abstract method does only the former.

Within BBB the call MyMethod() calls a method defined in AAA.

Because there is an override in BBB, calling that method results in an implementation in BBB being called.

Now, the definition in AAA informs calling code of two things (well, a few others too that don't matter here).

  1. The signature void MyMethod(string).
  2. (For those languages that support it) the default value for the single parameter is "aaa" and therefore when compiling code of the form MyMethod() if no method matching MyMethod() can be found, you may replace it with a call to `MyMethod("aaa").

So, that's what the call in BBB does: The compiler sees a call to MyMethod(), doesn't find a method MyMethod() but does find a method MyMethod(string). It also sees that at the place where it is defined there's a default value of "aaa", so at compile time it changes this to a call to MyMethod("aaa").

From within BBB, AAA is considered the place where AAA's methods are defined, even if overridden in BBB, so that they can be over-ridden.

At run-time, MyMethod(string) is called with the argument "aaa". Because there is a overridden form, that is the form called, but it is not called with "bbb" because that value has nothing to do with the run-time implementation but with the compile-time definition.

Adding this. changes which definition is examined, and so changes what argument is used in the call.

Edit: Why this seems more intuitive to me.

Personally, and since I'm talking of what is intuitive it can only be personal, I find this more intuitive for the following reason:

If I was coding BBB then whether calling or overriding MyMethod(string), I'd think of that as "doing AAA stuff" - it's BBBs take on "doing AAA stuff", but it's doing AAA stuff all the same. Hence whether calling or overriding, I'm going to be aware of the fact that it was AAA that defined MyMethod(string).

If I was calling code that used BBB, I'd think of "using BBB stuff". I might not be very aware of which was originally defined in AAA, and I'd perhaps think of this as merely an implementation detail (if I didn't also use the AAA interface nearby).

The compiler's behaviour matches my intuition, which is why when first reading the question it seemed to me that Mono had a bug. Upon consideration, I can't see how either fulfils the specified behaviour better than the other.

For that matter though, while remaining at a personal level, I'd never use optional parameters with abstract, virtual or overridden methods, and if overriding someone else's that did, I'd match theirs.

c# Optional Parameters in a virtual method and overridden method

Optional parameters are resolved at the callsite at compile time.

This means that v.GetPos() is really compiled to v.GetPos(false) because v is typed Piece. The fact that the call is virtual and ends up being resolved to Pawn.GetPos(bool) is irrelevant, that happens after the optional parameter has already been set.

That is why you get the output you are seeing.

Method Overriding and Optional Parameters

Okay, as there's general interest, here's a quick version:

Console.WriteLine(one)

This will use the WriteLine(object) overload, which will in turn execute the object.ToString() virtual method, overridden in One - hence the output of ToString override

Console.WriteLine(one.ToString())

This will look at One and see which methods have newly declared methods - discounting overrides. There's exactly one such method which is applicable - the one with the optional parameter. So that gets executed, using the default value, leading to output of ToString with optional parameter.

Console.WriteLine(one.ToString("foo"))

Same again, but this time the compiler doesn't need to use the default value, hence ToString with optional parameter foo

Console.WriteLine(two)

Again, this will call the virtual object.ToString() method from WriteLine(object). The method hasn't been overridden, so the default implementation returning the name of the type is used, leading to output of ConsoleApplication1.Test2.

Console.WriteLine(two.ToString())

The compiler looks at all the method declared in Two which aren't overriding virtual methods. In this case, there are two such methods - the parameterless one and the one with the optional parameter. The parameterless one is included because it's new rather than overriding a base class method.

The parameterless method is deemed a "better" candidate because the compiler prefers to call a method which doesn't need any optional parameters filling in. Hence the output is ToString new

Console.WriteLine(two.ToString("bar"))

Again, the compiler looks at all the method declared in Two which aren't overriding virtual methods. In this case, there are two such methods - but the parameterless one isn't applicable, leaving just the one with the optional parameter. The compiler doesn't need to use the default value of the optional parameter here, as it's got an argument anyway... so the output is ToString with optional parameter bar

For much more on this, read the C# language specification - or for a half-way house, see my article on overloading.

optional parameter: reversed precedence if override of overload exists

Why an overload with exact signature doesn't have precedence over another overload with optional parameters in case that the overload with exact parameters overrides a base implementation?

Basically this is the compiler following the rules of the specification, even though they're surprising in this case. (Section 7.6.5.1 is the relevant part of the C# 5 spec.)

The compiler looks at the "deepest" type first - i.e. the one with the compile-time type of the target (B in this case) and tries to find an applicable function member ignoring any methods which override those declared in a base class:

The set of candidate methods is reduced to contain only methods from the most derived types: For each method C.F in the set, where C is the type in which the method F is declared, all methods declared in a base type of C are removed from the set.

and:

The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected. If no method was found, try instead to process the invocation as an extension method invocation.

So in your case, the compiler only considers the newly-introduced method, finds it's applicable (using the default parameter value) and stops its search. (i.e. it doesn't look at methods declared in base classes). At that point, the set of candidate function members only has one entry, so there's no real overload resolution to perform at that point.

I have an article on overloading which shows this sort of thing, not using an optional parameter but using a different parameter type - see the "Inheritance" section.

C# : overriding Method with optional parameters & named parameters : Unexpected Result

Argument binding for the method call firstProcess.Calculate(x: 1, y: 2) is done at compile time, but method dispatch is done at runtime because the method is virtual.

In order to compile the method call, the compiler sees x: 1, y: 2 and needs to resolve this named argument list to a sequentially indexed argument list in order to emit the appropriate IL (push the arguments on the stack in the right order, then call method).

In addition to the named argument list there is one more piece of information available to the compiler: the static type of firstProcess, which is FirstProcess. Now me and you both know that at runtime this is going to be a SecondProcess instance, but the compiler doesn't know that (at least in the general case). So it looks up the parameter list of FirstProcess.Calculate and sees that x is the first argument, y is the second. This makes it compile your code as if you had written

firstProcess.Calculate(1, 2);

At runtime, the arguments 1 and 2 are pushed on the stack and a virtual call is made to Calculate. Of course this ends up calling SecondProcess.Calculate, but the parameter names have not survived the transition to runtime. SecondProcess.Calculate accepts 1 as its first argument (y) and 2 as its second argument (x), leading to the observed result.

As an aside, this is also what happens when you use default argument values:

public class FirstProcess
{
public virtual void Calculate(int x = 10)
{
Console.WriteLine("First Process X :{0}", x);
}
}

public class SecondProcess : FirstProcess
{
public override void Calculate(int x = 20)
{
Console.WriteLine("Second Process X :{0}", x);
}
}

var secondProcess = new SecondProcess();
var firstProcess = (FirstProcess) secondProcess;

secondProcess.Calculate(); // "Second Process X: 20"
firstProcess.Calculate(); // "Second Process X: 10"

The moral of the story: named and default arguments are convenient, but the way they are (necessarily) implemented leaves you open to unpleasant surprises. Use them when they offer real tangible benefits, not whenever you can.

Optional parameter in abstract class overriding derived

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.

Optional parameters and inheritance

What I also didn't realise, is that optional parameters don't change the method signature. So the following code is perfectly legal and was actually my solution:

interface IMyInterface
{
string Get(string str = "Default");
}

class MyClass : IMyInterface
{
public string Get(string str)
{
return str;
}
}

So if I have an instance of MyClass, I must call Get(string str), but if that instance has been declared as the base interface IMyInterface, I can still call Get(), which gets the default value from IMyInterface first, then invokes the method.

Why is my subclass required to override with default parameters?

I would expect it to accept a parameterless method signature, and then for it to be allowed to use the default parameter when base.MyMethod() is called.

Your expectation is incorrect. Adding a default for a parameter does not mean that a method without the parameter exists. It just injects the default value into any calling code. So there is no method with no parameter to override.

You could explicitly create both overloads in the base class:

protected virtual void MyMethod()
{
MyMethod(1);
}
protected virtual void MyMethod(int parameter)
{
Console.WriteLine(parameter);
}

Then you could override either overload, but it's not clear from your question if that's appropriate.

Optional parameters and method overloading

Well this is embarrassing and interesting in equal measures (okay - maybe slightly more embarrassing than interesting).

The compiler was correct (as it almost always is!). I had assumed that the compiler error was referring to the two M methods I outlined in the question, but the client code with the error was also overriding the original M with three parameters, and still providing the defaults to all three parameters.

I only discovered "the third M" when I actually added the [Obsolete] (sorry - I'd only posted it for illustration - didn't think that would be relevant) and got a warning about overriding an obsolete method.

I think this probably supports @p.s.w.g's comment!



Related Topics



Leave a reply



Submit