There are many articles on ref and out in C #. This article mainly analyzes the nature of out parameters from Il.
Directory
- Out = ref + parameterattributes. Out
- Call the ref and out methods through reflection
- Emit build out or ref Parameters
Returned directory
Out = ref + parameterattributes. Out
Let's look at two out and ref methods.
Static void test_ref (ref int I)
{}
Static void test_out (Out int I)
{I = default (INT );}
Il:
. Method private hidebysig static void test_ref (int32 & I) cel managed
. Method private hidebysig static void test_out ([out] int32 & I) cel managed
As you can see, the out parameter is a ref parameter (the address of all the passed parameters can be understood as a pointer in C/C ++), but a [out] is added.
This can also be verified from the attributes attribute of the parameterinfo class in reflection.
Public class Program
{
Static void main (string [] ARGs)
{
VaR reffunc = typeof (Program). getmethod ("test_ref ");
VaR outfunc = typeof (Program). getmethod ("test_out ");
Foreach (VAR Pa in reffunc. getparameters ())
Console. writeline (Pa. attributes );
Foreach (VAR Pa in outfunc. getparameters ())
Console. writeline (Pa. attributes );
}
Public static void test_ref (ref int I)
{}
Public static void test_out (Out int I)
{I = default (INT );}
}
Output:
None
Out
The parameterinfo attribute of the ref parameter is none, the value is equal to 0, and the out parameter is the out enumerated value.
Returned directory
Call the ref and out methods through reflection
Since out is a ref, there is no difference in reflection calls. Reflection calls require an object array as a parameter. The Compiler certainly does not allow you to put an uninitialized variable in an array, even if it is used as an out parameter. At the same time, as long as the array is created, the elements in it are null by default. No matter whether you want to replace null with other values, the final ref or out parameters will change the corresponding values.
Static void main (string [] ARGs)
{
VaR M = typeof (Program). getmethod ("Doo ");
VaR Arg = new object [] {null, null };
M. Invoke (null, ARG );
Console. writeline ("{0} {1}", Arg [0], Arg [1]);
}
Public static void doo (ref int A, out int B)
{
A = B = 1;
}
Output two 1 s. The values are modified correctly.
Returned directory
Emit build out or ref Parameters
First, create a dynamic method that is the same as the previous doo method using the typebuilder that reflects emit!
Based on the knowledge above, in the definemethod method of typebuilder, both parameters are reference parameters of INT (typeof (INT ). makebyreftype (). To set the second parameter to out, you must call methodbuilder. defineparameter method, set parameterattributes. out can be performed on the specified parameter!
Code:
Static void createmethod (typebuilder TB)
{
VaR mbuilder = Tb. definemethod ("Doo ",
Methodattributes. Public | methodattributes. hidebysig | methodattributes. Static,
Callingconventions. Standard,
Null,
New Type [] {typeof (INT). makebyreftype (), typeof (INT). makebyreftype ()});
// Both ref and out are typeof (INT). makebyreftype ()
// Set the ref parameter name to
Mbuilder. defineparameter (1, parameterattributes. None, "");
// Set the out parameter to B.
// Set parameterattributes. Out for parameter B (otherwise it is the same as the ref parameter)
Mbuilder. defineparameter (2, parameterattributes. Out, "B ");
VaR ilgen = mbuilder. getilgenerator ();
// A = B = 1
Ilgen. emit (Opcodes. ldarg_0 );
Ilgen. emit (Opcodes. ldc_i4_1 );
Ilgen. emit (Opcodes. stind_i4 );
Ilgen. emit (Opcodes. ldarg_1 );
Ilgen. emit (Opcodes. ldc_i4_1 );
Ilgen. emit (Opcodes. stind_i4 );
Ilgen. emit (Opcodes. Ret );
}
OK, use reflector to open the doo method of the assembly we generated using emit. You will see:
Everything is correct!