optional parameter: reversed precedence if override of overload exists - c#

I wonder about the following behavior:
public class A {
public virtual void Do() { Console.WriteLine("A"); }
}
public class B : A {
public override void Do() { Console.WriteLine("B override"); }
public void Do(int value = 0) { Console.WriteLine("B overload"); }
}
class Program {
public static void Main() {
new B().Do(); // ---> Console: "B overload"
}
}
I expect that an overload with the exact signature has precedence over another overload with optional parameters: I expect "B override" in the console. Instead the program writes "B overload" into the console.
Even resharper fails and falls into the trap:
... Resharper says that the overload with the optional parameter is hidden by the overload with the exact signature, but in fact it is the contrary.
Now, if you remove the inheritance, then it behaves as expected and resharper warning is legitimate:
public class B {
public virtual void Do() { Console.WriteLine("B override"); }
public void Do(int value = 0) { Console.WriteLine("B overload"); }
}
class Program {
public static void Main() {
new B().Do(); // ---> Console: "B override"
}
}
So the question: What is the precedence rule that explains this observation? 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?

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.

Related

Why does this function call resolve to the base class method, casting the int to a double?

Why does this program print "abc from B" instead of "abc from A"? My intuition says that it should resolve the method to the more specific type parameter (which is how I've always used overloading).
public class Program
{
public static void Main(string[] args)
{
int i = 5;
B b = new B();
b.Method1(i);
Console.ReadLine();
}
}
class A
{
public void Method1(int q)
{
Console.WriteLine("abc from A");
}
}
class B : A
{
public void Method1(double p)
{
Console.WriteLine("abc from B");
}
}
Overload resolution doesn't work across types.
This is due to one crucial step in determining the set of candidate methods for overload resolution, as stated in the language spec:
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.
Basically, the A.Method1 isn't even considered for overload resolution.
There are a few other steps to this, but the spec nicely summarises the process:
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.
Interestingly, your intuition that A.Method1 should be called because there is an identity conversion from int to int is how it works in Java :)
Because you called it from a variable of the B type. If you cast b as an A, you'd get the first result. See it here:
https://dotnetfiddle.net/TAYfJX
If you want it to do overloading based on argument type, put both versions of the method in the same type.

Strange better function member behavior [duplicate]

I can surely answer to this question by myself writing a dummy test but I want to know what people think about the question. Here it is:
Which method will be call when we have both at the same time overloading and overriding? I am only considering Type overloading and not arity overloading and when Type the overload are related.
Let me throw you an example:
class AA {}
class BB : AA {}
class A {
public virtual void methodA(AA anAA) { Console.Write("A:methodA(AA) called"); }
public virtual void methodA(BB aBB) { Console.Write("A:methodA(BB) called"); }
}
class B : A {
public override void methodA(AA anAA) { Console.Write("B:methodA(AA) called"); }
}
new B().methodA(new BB()); // Case 1
new B().methodA(new AA()); // Case 2
new B().methodA((AA)new BB()); // Case 3
Can you tell what will happen in case 1, 2, and 3?
I personally think that overloadaing is evil and that there is no consistent thinking that could lead to a predictable answer. And that is completely base on a convention implemented in the compiler+vm.
EDIT: If you have some doubt about why overload is evil you can read the blog post from Gilad Brach
Thanks
No, it is entirely predictable. The method signature is resolved first - that is, the overload is determined first. Then, the most overridden method is called. So the output will be:
A:methodA(BB) called
B:methodA(AA) called
B:methodA(AA) called
The method taking an instance of AA will be called in the second two cases, because this is the type of the reference that is passed in, and it is B's version that is called. Note that even this would produce the same result:
A instance = new B();
instance.methodA((AA)new BB()); // Case 3
Overridden methods are excluded from method set when compiler determines which method to call. See member lookup algorithm. So, when you call methodA on type B, set of members with name methodA from type B and it's base type will be constructed:
override B.methodA(AA)
virtual A.methodA(AA)
virtual A.methodA(BB)
Then members with ovveride modifier removed from set:
virtual A.methodA(AA)
virtual A.methodA(BB)
This group of methods is the result of lookup. After that overload resolution applied to define which member to invoke.
A.methodA(BB) is invoked, because its argument matches parameter.
A.methodA(AA) will be chosen, but it is virtual method, so actually call goes to B.method(AA)
Same as option 2
I think the result will be this
case 1 : Console.Write("A:methodA(BB) called");
case 2 : Console.Write("B:methodA(AA) called");
case 3 : Console.Write("B:methodA(AA) called");
in case 3 it will look the type that it's passed, and it's B

The call is ambiguous between the following methods or properties (one static and one non-static)

Why am I not allowed to have a static and non-static methods with the same signature?
Let's say I have a class like this
public class TestClass
{
public object thing { get; set; }
public TestClass()
{
}
public TestClass(object thing)
{
this.thing = thing;
}
public static TestClass ConvertTestClass(object thing)
{
return new TestClass(thing);
}
public TestClass ConvertTestClass(object thing)
{
this.thing = thing;
return this;
}
}
and I try to use it like this
public class SomeOtherClass
{
public SomeOtherClass()
{
TestClass tc = TestClass.ConvertTestClass(new object());
TestClass tc2 = new TestClass();
tc2.ConvertTestClass(new object());
}
}
I get the following errors on TestClass.ConvertTestClass(new object());
The call is ambiguous between the following methods or properties: 'TestClass.ConvertTestClass(object)' and 'TestClass.ConvertTestClass(object)'
and these errors on tc2.ConvertTestClass(new object());
The call is ambiguous between the following methods or properties: 'TestClass.ConvertTestClass(object)' and 'TestClass.ConvertTestClass(object)'
Member 'TestClass.ConvertTestClass(object)' cannot be accessed with an instance reference; qualify it with a type name instead
Can the compiler really not tell the difference between the static and non static versions of that method or am I missing something here?
I am not using ReSharper (which seemed to be the root of a similar problem in other questions).
Its giving you an error, so its a safe bet that the compiler can't, or won't, discern between the two methods.
Its probably a bad idea to do this kind of overload anyways, as it's unclear which method you are intending to invoke, but if that isn't enough, the C# 5 specification defines a method signature like this (Section 3.6):
The signature of a method consists of the name of the method, the
number of type parameters and the type and kind (value, reference, or
output) of each of its formal parameters, considered in the order left
to right. For these purposes, any type parameter of the method that
occurs in the type of a formal parameter is identified not by its
name, but by its ordinal position in the type argument list of the
method. The signature of a method specifically does not include the
return type, the params modifier that may be specified for the
right-most parameter, nor the optional type parameter constraints.
It doesn't explicitly mention static, but it also doesn't include it as part of the definition of a "signature". Thus, its fair to assume that by the spec, a method overload cannot differ only in the fact that it is static or instanced.
I'd write this as a comment however it's easier to make this point in a proper editor.
I think you're only thinking about the logic of calling methods on the class externally i.e. from another class. Within the class methods with the same signature only differing by static doesn't make any sense. e.g you have a class with two methods as follows
public class MyClass
{
public static void HellowWorld()
{
Console.WriteLine("Hello World!");
}
public void HellowWorld()
{
Console.WriteLine("Howdy World!");
}
public void Greet()
{
HellowWorld();
}
}
When compiling you'll see as long as one of the methods is commented out it compiles without errors. You should be able to alternate the commented out method and compile the class succesfully. Indicating there's no way of differentiating which method should be called within the scope of the class.
You could argue that within the class you should be forced to use the same syntax to call a static method as you do externally e.g.
MyClass.HelloWorld();
However, this would defy scoping logic used throughout C#, why should you need to specify the class name within a class? I think such a change would also create ambiguity where the was none, and to do so now would of course break a lot of code out there.
I think the compiler logic as it is makes perfect sense.

C# overload with override

I can surely answer to this question by myself writing a dummy test but I want to know what people think about the question. Here it is:
Which method will be call when we have both at the same time overloading and overriding? I am only considering Type overloading and not arity overloading and when Type the overload are related.
Let me throw you an example:
class AA {}
class BB : AA {}
class A {
public virtual void methodA(AA anAA) { Console.Write("A:methodA(AA) called"); }
public virtual void methodA(BB aBB) { Console.Write("A:methodA(BB) called"); }
}
class B : A {
public override void methodA(AA anAA) { Console.Write("B:methodA(AA) called"); }
}
new B().methodA(new BB()); // Case 1
new B().methodA(new AA()); // Case 2
new B().methodA((AA)new BB()); // Case 3
Can you tell what will happen in case 1, 2, and 3?
I personally think that overloadaing is evil and that there is no consistent thinking that could lead to a predictable answer. And that is completely base on a convention implemented in the compiler+vm.
EDIT: If you have some doubt about why overload is evil you can read the blog post from Gilad Brach
Thanks
No, it is entirely predictable. The method signature is resolved first - that is, the overload is determined first. Then, the most overridden method is called. So the output will be:
A:methodA(BB) called
B:methodA(AA) called
B:methodA(AA) called
The method taking an instance of AA will be called in the second two cases, because this is the type of the reference that is passed in, and it is B's version that is called. Note that even this would produce the same result:
A instance = new B();
instance.methodA((AA)new BB()); // Case 3
Overridden methods are excluded from method set when compiler determines which method to call. See member lookup algorithm. So, when you call methodA on type B, set of members with name methodA from type B and it's base type will be constructed:
override B.methodA(AA)
virtual A.methodA(AA)
virtual A.methodA(BB)
Then members with ovveride modifier removed from set:
virtual A.methodA(AA)
virtual A.methodA(BB)
This group of methods is the result of lookup. After that overload resolution applied to define which member to invoke.
A.methodA(BB) is invoked, because its argument matches parameter.
A.methodA(AA) will be chosen, but it is virtual method, so actually call goes to B.method(AA)
Same as option 2
I think the result will be this
case 1 : Console.Write("A:methodA(BB) called");
case 2 : Console.Write("B:methodA(AA) called");
case 3 : Console.Write("B:methodA(AA) called");
in case 3 it will look the type that it's passed, and it's B

What makes a name of a method equivalent to an action delegate?

I was just experimenting and ended up with the following snippet:
public static class Flow {
public static void Sequence(params Action[] steps) {
foreach (var step in steps)
step();
}
}
void Main() {
Flow.Sequence(() => F1(), () => F2());
Flow.Sequence(F1, F2); // <-- what makes this equiv to the line above?
}
void F1() { }
void F2() { }
I didn't realize that a method name alone was the same as an Action.
What is making this so?
In C#, delegates are nothing more than method pointers. They can point to existing methods in a class, or independent anonymous delegate objects altogether.
This paragraph from the above link should explain what's happening in your code:
Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.
That is, when resolving delegate types, what's considered are their signatures, rather than their names.
In your case, your F1() and F2() methods, taking no parameters and returning nothing, have matching signatures with the parameterless Action delegate:
public delegate void Action();
Therefore, they're implicitly convertible to Action.
If you try to pass a method with a different return type or at least one parameter, you'll get a compile-time error as it won't correspond to Action's signature.
Basically, this is kind of what is happening in the background:
void Main()
{   
Flow.Sequence(new Action(delegate(){ F1(); }), new Action(delegate(){ F2(); }));   
Flow.Sequence(new Action(F1), new Action(F2));
}
They're not EXACTLY equivalent, but they're very close. They would render the same results at run-time, the only difference being that the arguments in the first Sequence invocation would be an Action which invokes an anonymous method which then invokes the static methods F1 and F2; the second Sequence invocation would be an Action which invokes the static methods F1 and F2.
I hope this helps.
The compiler uses an implicit conversion from a method group to a delegate of compatible type (in this case a void returning method taking no arguments), the method names here are irrelevent.

Categories