.NET(C#):從IL和反射Emit更徹底地理解out參數

來源:互聯網
上載者:User

C#中ref和out的文章有很多,這篇文章主要從IL上分析out參數的本質。

 

目錄

  • out = ref + ParameterAttributes.Out
  • 反射調用ref和out方法
  • Emit構建out或ref參數

 

 

返回目錄

out = ref + ParameterAttributes.Out

看兩個out和ref的方法。

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) cil managed

 

.method private hidebysig static void test_out([out] int32& i) cil managed

 

可以看到,out參數是ref參數(都是傳的參數的地址,可以理解成C/C++中的指標),只不過加了一個[out]。

這個也可以從反射中的ParameterInfo類的Attributes屬性驗證。

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); }

 

}

 

輸出:

None

Out

 

ref參數的ParameterInfo屬性是None,數值上等於0,而out參數是Out枚舉值。

 

返回目錄

反射調用ref和out方法

由於out就是ref,所以反射調用沒有區別,反射調用需要一個object數組作參數,編譯器肯定不允許你把一個未初始化的變數放在數組裡,即便是它要當做out參數。同時只要數組被建立,裡面的元素預設是null的,不管你是否吧null替換成其他值,最終ref或out參數都會修改相應值的。

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;

}

 

輸出兩個1。值都被正確修改了。

 

 

返回目錄

Emit構建out或ref參數

先在用反射Emit的TypeBuilder建立一個和上例doo一樣的動態方法!

 

結合上面的知識,在TypeBuilder的DefineMethod方法上,兩個參數都是int的引用參數(typeof(int).MakeByRefType()),要想把第二個參數設定成out,則需要調用MethodBuilder.DefineParameter方法,設定ParameterAttributes.Out在指定參數上才可以!

 

代碼:

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() });

    //ref和out都是typeof(int).MakeByRefType()

 

    //設定ref參數名稱為a

    mbuilder.DefineParameter(1, ParameterAttributes.None, "a");

    //設定out參數名稱為b

    //並且為參數b設定ParameterAttributes.Out(否則它和ref參數一樣)

    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,用Reflector開啟我們用Emit產生的程式集的這個doo方法,你會看到:

 

 

一切正確!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.