The order of return and finally execution in try

Source: Internet
Author: User
Tags finally block

1 finally and return
Try-catch-finally is a common syntax structure used to control the program flow in case of exceptions. catch and finally must have at least one. When learning the try syntax, you may have to ask the following question: if return is returned in the try block, will the finally statement be executed? The answer is yes. This is very easy to verify, so we will not give an example. This brings about some good features. For example, we can try to open the database in the try block, then read the data, and then directly return the obtained data, to close the data connection, run finally. finally first checks whether the database is opened normally and then closes it. In this way, the code is clearly written, and each part performs its own tasks. In this way, we can say with certainty that the finally content must be executed no matter what happens (as long as the process is not forcibly killed.
So can I ask another question -- what if return is written in the finally block? It is easy to know from the test that the return statement cannot be written in the finally block. If the return statement must be written, a compilation error will be returned:
Error CS0157: Control cannot leave the body of a finally clause
 
2 first return? First finally?
Since finally must be executed, even if there is return in the try block, what is the execution of the two? A simple experiment (the results of this experiment are not as intuitive as they appear below ):


Using System;
Public class TestClass1
...{
Public static void Main ()
...{
Console. WriteLine ("{0}", Func1 ());
}
 
Public static int Func1 ()
...{
Int a = 1;
 
Try
...{
Return;
}
Finally
...{
A ++;
}
}
}
Run this program and the result is easily "1 ". Then it seems that the return is executed first, and finally is later. Is that true?
In this example, a to return is a value type. What will happen if it is a reference type?


Using System;
Public class TestClass2
...{
Public int value = 1;
}
 
Public class TestClass1
...{
Public static void Main ()
...{
Console. WriteLine ("{0}", Func2 (). value );
}
 
Public static TestClass2 Func2 ()
...{
TestClass2 t = new TestClass2 ();
 
Try
...{
Return t;
}
Finally
...{
T. value ++;
}
}
}
The result of this operation is not 1, but 2. Obviously, the result returned by running Func2 () is not simply t written after return, but t whose value changes after finally block execution. How can we explain this difference?
3. CLR Stack
To explain this difference, we need to look at what its IL is and understand it from the perspective of calling functions and parameter stacks. The CLR also has stacks in execution, but the stack is used differently from the stack in traditional local code. The stack in local code is very useful. It can be used not only to temporarily Save the register value, but also to save local variables. In addition, it can be used to save some or all parameters passed to the function, function return values are generally transmitted through the EAX register, rather than the stack. However, in CLR, local variables are not explicitly stored using stacks, while stacks are only used to pass parameters when calling functions. In addition, function return values are also stored using stacks. When a function is called, the parameters required by the function are pushed to the stack in sequence, and these parameters are directly used in the function. When the function returns, the return value is pushed to the stack. After the function returns, the stack top is the return value. If the caller does not care about the return value, execute the pop statement to bring up the return value. This ensures that the function is at the same position on the top of the stack before and after the call.
When passing parameters through the pressure stack, the type of parameters is different, and the content of the Pressure stack is also different. If it is a value type, the pressure stack is the copied parameter value, if it is a reference type, then the stack is only a reference, which is what we are familiar, when the value type is passed, modifying the parameter value in the function does not affect the function, but the referenced type does.
In the Code, when we execute new, the corresponding IL is newobj, and the result is to create a TestClass2 object and return a reference and place it on the stack, the subsequent stloc saves the reference as a local variable, so there is no other content on the stack. The Try block does not perform too many operations. It just places the saved reference on the stack and saves it as another local variable. This local variable is the reference to be returned later, at this time, we have two local variables, but they point to two references of the same object. The Finally block is first taken out of the reference saved at the beginning and placed on the stack. The dup statement adds a identical reference at the top of the stack, then the ldquo statement is to take a member from the top object of the stack and place it on the stack. The obtained member is value, and then press 1 to the stack, and then execute add, the process of 1 + 1 = 2 is realized. add pops up two values from the stack, and then presses a value back to the stack. Then, call stfld to set 2 of the stack to the value attribute of the referenced object under 2 of the stack. The finally part is the true return, which tries to extract the second local variable we saved and press the stack to use it as the return value. But for the reference type, it refers to the same object as the reference of the previous operation. Therefore, the operation in the finally block will affect the return value, which is very easy to understand.
 
4. Adaptation
Knowing the implementation principles of finally and return is not difficult to further promote. For example, if you change the program to this (from directly returning t to calling a function to do some operations on t), you can easily guess the execution result:

Using System;
Public class TestClass2
...{
Public int value = 1;
 
Public TestClass2 Double ()
...{
Value * = 2;
Return this;
}
}
 
Public class TestClass1
...{
Public static void Main ()
...{
Console. WriteLine ("{0}", Func2 (). value );
}
 
Public static TestClass2 Func2 ()
...{
TestClass2 t = new TestClass2 ();
 
Try
...{
& N

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.