Without hesitation, let's just start off with a code sample. When running this program, what output would you expect?

using System;
namespace Alcedo.Demo
{
    class Base { }
    class Derived : Base { }

    class Worker
    {
        public virtual void Work(Derived param)
        {
            Console.WriteLine("Worker.Work(Derived)");
        }
    }

    class SpecializedWorker : Worker
    {
        public override void Work(Derived param)
        {
            Console.WriteLine("SpecializedWorker.Work(Derived)");
        }

        public void Work(Base param)
        {
            Console.WriteLine("SpecializedWorker.Work(Base)");
        }
    }

    class Program
    {
        public static void Main()
        {
            Derived param = new Derived();
            SpecializedWorker worker = new SpecializedWorker();
            worker.Work(param);
        }
    }   
}

Think about it for a while before reading on.

.

.

.

.

.

.

.

That should do. The output is SpecializedWorker.Work(Base). If that was what you expected (and you know why) you can stop reading here. If not, keep reading for the explanation.

The decision of what method to call can happen in one of two places; either it is done by the compiler, or it is done by the run-time. Well, the compiler is always involved, but in the case of virtual methods, it is ultimately the run-time that decides, based on the actual run-time types of the objects involved. That reasoning seems to indicate that the output of the program really ought to be SpecializedWorker.Work(Derived). After all, we invoke a method called Work on an object of the type SpecializedWorker, passing an object of the type Derived to it. Surely the run-time should have called SpecializedWorker.Work(Derived)?

It turns out the run-time is not even involved in the decision.

Given the code above, the compiler will make an attempt to decide what method to call. This follows a rather detailed specification (that you can read here: C# Language Specification, Method invocations).

In brief, it starts off by identifying all possible candidate methods. A method is a candidate if it:

  • Has the same name (in our case Work)
  • It is declared in the type (in our case SpecializedWorker) or one of its base types
  • It is not declared with the override modifier
At this point there are two candidates to choose from:
public virtual void Worker.Work(Derived)
public void SpecializedWorker.Work(Base)

OK, so at this point SpecializedWorker.Work(Derived param) is not in the candidate list any longer. In fact calls to override methods should never be; they are called as a result of overload resolution at run-time, not based on a decision of the compiler. It would still be called if the compiler decided on using the virtual Worker.Work(Derived) method. But the compiler does not choose the virtual method. Why?

The answer is found in the Method Invocations document linked above:

If N is applicable with respect to A (Section 7.4.2.1), then all methods declared in a base type of T are removed from the set.

According to the section linked above, a method is considered to be applicable if the number of arguments to the method is identical to the argument list in the call, and if there is an implicit conversion from each argument's type, to the corresponding argument of the method.

Both candidates meet this requirement; both have exactly one argument, and the type Derived can implicitly be converted to the argument type of each of the two methods. The result of the quoted text from the specification is that in this case the virtual Worker.Work(Derived) method will be removed from the list of candidates since it is declared in a base type of SpecializedWorker.

And now the list of candidates is reduced to one single method: SpecializedWorker.Work(Base).

kick it on DotNetKicks.com


I am working for a great consultancy company called Diversify, located in Sweden. We are hiring skilled .NET developers. If you are interested, don't hesitate to get in touch with me.

Kids sometime play something known as the "Chinese whispers". One person whispers something into the ear of the person at his or her side, that person repeats the message to the next person and so on. In the end the message returns, often distorted, to the person who started.

Luckily, code (typically) does not interpret the message, but rather passes it on in exactly the same form. C# allows chaining assignments in a way that at first glance looks like a variation of the whispering game (but without the fun of the distorted message):

int x, y z;
x = y = z = 42;

The result of the code above is that x, y and z will all have the value 42. So far it seems quite straightforward, right?

Now, consider the following code:

class Foo
{
    public int Value { private get; set; }
}

class Program
{
    public static void Main(string[] args)
    {
        Foo x = new Foo();
        Foo y = new Foo { Value = x.Value = 5 };
        Console.WriteLine(y);
    }
}

Do you see anything odd? x.Value is assigned the value of 5, but what is assigned to y.Value? If you examine the code, you will see that Foo.Value is write-only from outside the Foo class, so the code in Program.Main can't possibly read the value from x.Value in order to assign it to y.Value.

What happens behind the scenes here is that the value on the right-hand side of the expression (in this case the integer 5) is pushed onto the evaluation stack once for each assignment found to its left. Then each assignment is performed by popping the next value off the evaluation stack. So in the example Foo y = new Foo { Value = x.Value = 5 }; the integer 5 is pushed twice onto the evaluation stack, the code pops a value off the stack and assigns it to x.Value and then it pops the next value off the stack and assigns it to y.Value.

So in contrast to the Chinese whispers game, chained assignments is more like the first person whispering the message to each of the other persons directly.

kick it on DotNetKicks.com


I am working for a great consultancy company called Diversify, located in Sweden. We are hiring skilled .NET developers. If you are interested, don't hesitate to get in touch with me.