By calling Push() and Pop() an instance of Stack<T> in a single line I get a different behavior than performing the imho same code in two lines.
The following code snippet reproduces the behavior:
static void Main(string[] args)
{
Stack<Element> stack = new Stack<Element>();
Element e1 = new Element { Value = "one" };
Element e2 = new Element { Value = "two" };
stack.Push(e1);
stack.Push(e2);
Expected(stack); // element on satck has value "two"
//Unexpected(stack); // element on stack has value "one"
Console.WriteLine(stack.Peek().Value);
Console.ReadLine();
}
public static void Unexpected(Stack<Element> stack)
{
stack.Peek().Value = stack.Pop().Value;
}
public static void Expected(Stack<Element> stack)
{
Element e = stack.Pop();
stack.Peek().Value = e.Value;
}
The Element class is really basic:
public class Element
{
public string Value
{
get;
set;
}
}
With this code I get the following result (.NET 3.5, Win 7, fully patched):
- Calling
Expected()(version with
two lines) leaves one element on the
stack withValueset to"two". - When calling
Unexpected()(Version
with one line) I get one element on
the stack with theValueset to
"one".
The only reason for the difference I could imagine was the operator precedence. As the assignment operator (=) has the lowest precedence I see no reason why the two method should behave differently.
I also had a look at the IL generated:
.method public hidebysig static void Unexpected(class [System]System.Collections.Generic.Stack`1<class OperationOrder.Element> stack) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: callvirt instance !0 [System]System.Collections.Generic.Stack`1<class OperationOrder.Element>::Peek()
L_0006: ldarg.0
L_0007: callvirt instance !0 [System]System.Collections.Generic.Stack`1<class OperationOrder.Element>::Pop()
L_000c: callvirt instance string OperationOrder.Element::get_Value()
L_0011: callvirt instance void OperationOrder.Element::set_Value(string)
L_0016: ret
}
I’m not an IL crack, but for me this code still looks good ans should leave one element on the stack with value set to “two”.
Can anyone explain me the reason why the method Unexpected() does something different than Expected()?
Thanks a lot!
Lukas
Your expression is equivalent toWhen calling an instance method on a reference type, the instance is evaluated first.
EDIT: As Eric Lippert points out, all expressions are evaluated left-to-right.