5

So I have just caught this bug in our code:

class A
{
    public int a;
}
var x = new A();
x.a = 1;
A qwe(ref A t) {
    t = new A();
    t.a = 2;
    return t;
}
void asd(A m, A n) {
    Console.WriteLine(m.a);
    Console.WriteLine(n.a);
}

asd(x, qwe(ref x));
asd(x, qwe(ref x));

Is the order of execution in function invocation specified regarding the order of parameters?

What is written here is:

1
2
2
2

Which means that the first parameter's reference is saved before the second parameter's function is invoked.

Is this defined behavior? I could not find specific information on the order of execution in C# lang spec.

ditoslav
  • 4,563
  • 10
  • 47
  • 79
  • Looks how I'd expect, x starts as A with value 1, you then run a function which changes x to 2, so it stays 2 for the rest of your test... its like A(x,++x) I guess – BugFinder Jan 10 '17 at 12:57
  • @BugFinder The OPs point being, why isn't that first 1 a 2 though - seemingly `qwe` has run (and therefore updated `x`) before `asd` is called. – James Thorpe Jan 10 '17 at 12:58
  • Well `x` initially refers to the original A object and that reference is pushed onto the stack before the call to `qwe()` changes `x` to reference a different object. Thus, `asd()` is called with two different references. – Matthew Watson Jan 10 '17 at 13:16
  • @JamesThorpe the output shows 1,2 2,2 so it did have 1 and then a 2.. – BugFinder Jan 10 '17 at 13:18
  • @BugFinder The OP is wondering _why_ it's 1,2,2,2 though, and not 2,2,2,2 - ie where in the spec is this behaviour defined. – James Thorpe Jan 10 '17 at 13:19
  • k, Id never have expected it to be, so, I guess thats why i didnt read it to be .. my bad – BugFinder Jan 10 '17 at 13:20
  • See https://blogs.msdn.microsoft.com/ericlippert/tag/precedence/ if this subject interests you. – Eric Lippert Jan 10 '17 at 13:48

1 Answers1

8

C# requires that parameter expressions passed to methods be evaluated left-to-right.

Even though the qwe finishes its work prior to invoking asd, C# has captured the reference to "old" A before invoking qwe in preparation for the call. That is why the first argument of the first invocation of asd gets the "old" A object, before it gets replaced with the new A object inside the qwe call.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    Is this defined behavior or just happens to occur? I know c++ and c do not specify what happens in this case – ditoslav Jan 10 '17 at 13:22
  • 2
    @DominikDitoIvosevic There is very little in C# that's unspecified ([See this Q&A with an answer from the former team lead of C# compiler team](http://stackoverflow.com/a/1860953/335858)). This particular behavior [is fully specified in section 7.5.1.2 of the language specification](http://stackoverflow.com/a/7360045/335858). – Sergey Kalinichenko Jan 10 '17 at 13:28
  • 1
    @dasblinkenlight: Just to clarify, I was never a "lead"; leads have people reporting to them and I never have had reports. I was the confusingly-named "principal developer", which is just an arbitrary level title. I had plenty of people above me in the dev hierarchy: some "partner architects" and one "technical fellow". Titles at software companies are weird. – Eric Lippert Jan 10 '17 at 13:41
  • 1
    The answer here is basically right, but I would add that there have over the years been situations where there are rare cases where you can observe a "ref" argument being evaluated in not strictly left-to-right order. For example, there was a bug where the code generator for methods with named arguments would get the ref semantics slightly wrong. There have been corner cases where a ref that would throw an exception -- say, taking a ref to a "bad" offset in a 2-d array -- could be observed to throw with "wrong" ordering. And so on. But these scenarios should be rare. – Eric Lippert Jan 10 '17 at 13:46
  • @EricLippert Thanks for clarification! I worked mostly for companies with flat reporting structures, so I got confused about titles in Microsoft. – Sergey Kalinichenko Jan 10 '17 at 13:50