Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why adding a return type to a void returning method causes a MissingMethodException

Tags:

c#

.net

clr

I have a .NET application that uses an assembly (.dll) that defines some method:

    public void DoSomething()
    {
        // Do work
    }

Suppose this method signature changes to include a string return type:

    public string DoSomething()
    {
        // Do work
        return "something";
    }

Why does the code that uses this method fails on a System.MissingMethodException ?

It seems to me, that at all call sites to this method, no use was made of the return value (since it did not exist before).

Why does this change break the code then?

like image 834
lysergic-acid Avatar asked Feb 07 '12 15:02

lysergic-acid


1 Answers

The other answers which state that you've changed the signature of a method, and therefore must recompile the callers, are correct. I thought I might add some additional information about this bit of your question:

It seems to me, that at all call sites to this method, no use was made of the return value (since it did not exist before).

That is exactly right. Now, consider this question: how do you write code that makes no use of data? You seem to be labouring under the entirely false assumption that not using a value requires no code, but not using a value certainly does require code!

Suppose you have methods:

static int M1(int y) { return y + 1; }
static void M2(int z) { ... }

and you have a call

int x;
x = M1(123);

What happens at the IL level? the following:

  • Allocate space on the temporary pool for x.
  • Push 123 on the stack
  • Invoke M1.
  • Push 1 on the stack. Stack is now 1, 123
  • Add the top two things on the stack. This pops both and pushes the result. Stack is now 124.
  • Return to the caller
  • The stack is still 124.
  • Store the value on the stack into the temporary storage for x. This pops the stack, so the stack is now empty.

Suppose you now do:

M1(345);

What happens? Same thing:

  • Push 345 on the stack
  • Invoke M1.
  • Push 1 on the stack. Stack is now 1, 345
  • Add the top two things on the stack. This pops both and pushes the result. Stack is now 346.
  • Return to the caller
  • The stack is still 346.

But there is no instruction which stores the value on the stack anywhere, so we have to issue a pop instruction:

  • Pop the unused value off the stack.

Now suppose you call

M2(456);

What happens?

  • Push 456 on the stack
  • Invoke M2.
  • M2 does its thing. When it returns to the caller, the stack is empty because it is void returning.
  • The stack is now empty, so do not pop anything off it.

Now do you see why changing a method from void returning to value returning is a breaking change? Every caller now has to pop the unused value off the stack. Doing nothing with data still requires cleaning it up off the stack. You are misaligning the stack if you do not pop that value off; the CLR requires the stack to be empty at the beginning of every statement to ensure that this sort of misalignment does not happen.

like image 184
Eric Lippert Avatar answered Sep 19 '22 12:09

Eric Lippert