Why adding a return type to the void return method throws a MissingMethodException

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

public void DoSomething() { // Do work } 

Suppose that this method signature changes the type value of the return type:

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

Why does code using this method fail in a System.MissingMethodException?

It seems to me that on all call sites for this method, the return value was not used (since it did not exist before).

Why does this change interrupt code?

+8
c # clr
source share
5 answers

Because you made a change of api. You either changed the method signature in the base class or in the interface.

Subscribers are associated with this method. In IL, a method reference is not only a reference to a type and its method with some index to a method, but the method’s references to the callers include the full signature of the method.

This change, therefore, can be fixed by recompiling all the assemblies that call this method, but you get runtime exceptions when you only recompile the changed assembly and hope that the assemblies used will magically raise the changed method signature. This is not so because the method reference contains the full method signature and type of definition.

It happened to me. You are right that no one can use the return type to make this change safe, but you need to recompile all the goals that are affected.

+1
source share

Other answers stating that you changed the method signature and therefore must recompile the callers are correct. I thought I could add more information about this question:

It seems to me that on all call sites for this method, the return value was not used (since it did not exist before).

That's for sure. Now consider this question: how do you write code that does not use data? You seem to be working under the completely false assumption that without using a value that does not require code, but without using a value, the code certainly requires!

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? following:

  • Allocate space in the temporary pool for x.
  • Click 123 on the stack
  • Call M1.
  • Press 1 on the stack. The stack is now 1, 123
  • Add two vertices to the stack. This pops up and pushes the result. The stack is now 124.
  • Return to Caller
  • Stack another 124.
  • Store the value on the stack into temporary memory for x. This pushes the stack, so the stack is now empty.

Suppose you now run:

 M1(345); 

What's happening? Same:

  • Click 345 on the stack
  • Call M1.
  • Press 1 on the stack. The stack is now 1, 345
  • Add two vertices to the stack. This pops up and pushes the result. The stack is now 346.
  • Return to Caller
  • There are 346 on the stack.

But there is no instruction that stores the value on the stack anywhere, so we need to issue the pop command:

  • Pop the unused value from the stack.

Now suppose you call

 M2(456); 

What's happening?

  • Press 456 on the stack
  • Call M2.
  • M2 does its thing. When it returns to the caller, the stack is empty because it is invalid.
  • The stack is now empty, so do not delete anything.

Now you see why a change of method from void returning to a return of value is a violation of the changes? Each caller must pull an unused value from the stack . Doing nothing with the data still requires clearing it from the stack. You move the stack if you do not throw this value; The CLR requires that the stack be empty at the beginning of each statement to ensure that this kind of misalignment does not occur.

+14
source share

If the reflection is not connected, but the link is static (I assume this is from the description), then the runtime will try to find the method using the exact signature. This is how CIL callvirt works.

It does not matter if this value is consumed - the runtime cannot find void YourClass::DoSomething() , and does not even try to find the string YourClass::DoSomething() .

If such a change was possible, you could easily explode the runtime by causing a stack overflow / overflow.

+2
source share

Because you changed the method signature.

When an external code needs to find a method, it needs to make sure that it calls correctly. It stores some of this information as a signature during compilation - the signature information includes a return type (regardless of whether it is used anywhere else).

As for the CLR, the method with the void return type no longer exists - hence, MissingMethodException .

+1
source share

The creators of C # decided that they should be strict regarding method signatures.

0
source share

All Articles