C# 關鍵字區別ref 和 out(文章來源:http://www.tzwhx.com/newOperate/html/1/12/123/19058.html)

 

 

 

 

C#關鍵字區別之ref和out

問題:爲什麼c#中要有ref和out?(而java中沒有)
需求假設:現需要通過一個叫Swap的方法交換a,b兩個變量的值。交換前a=1,b=2,斷言:交換後a=2,b=1。

現編碼如下:

 1class Program
 2    {
 3        static void Main(string[] args)
 4        {
 5            int a = 1;
 6            int b = 2;
 7            Console.WriteLine("交換前/ta={0}/tb={1}/t",a,b);
 8            Swap(a,b);
 9            Console.WriteLine("交換後/ta={0}/tb={1}/t",a,b);
10            Console.Read();
11        }

12        //交換a,b兩個變量的值
13        private static void Swap(int a,int b)
14        {
15            int temp = a;
16            a = b;
17            b = temp;
18            Console.WriteLine("方法內/ta={0}/tb={1}/t",a,b);
19        }

20    }

 

運行結果:

              交換前  a = 1    b = 2

              方法內  a = 2    b = 1

              交換後  a = 1    b = 2

斷言失敗,並未達到我們的需求!

原因分析:int類型爲值類型,它存在於線程的堆棧中。當調用Swap(a,b)方法時,相當於把a,b的值(即1,2)拷貝一份,然後在方法內交換這兩個值。交換完後,a還是原來的a,b還是原來的b。這就是C#中按值傳遞的原理,傳遞的是變量所對應數據的一個拷貝,而非引用。

解決方案:因此,C#中提出了ref 和out兩個關鍵字。

修改代碼如下即可實現我們想要的結果:

 1class Program
 2    {
 3        static void Main(string[] args)
 4        {
 5            int a = 1;
 6            int b = 2;
 7            Console.WriteLine("交換前/ta={0}/tb={1}/t",a,b);
 8            Swap(ref a,ref b);
 9            Console.WriteLine("交換後/ta={0}/tb={1}/t",a,b);
10            Console.Read();
11        }

12        //交換a,b兩個變量的值
13        private static void Swap(ref int a, ref int b)
14        {
15            int temp = a;
16            a = b;
17            b = temp;
18            Console.WriteLine("方法內/ta={0}/tb={1}/t",a,b);
19        }

20    }

 

同理用out同樣可以實現我們的需求。

下面談談ref和out到底有什麼區別:

1 關於重載

   原則:有out|ref關鍵字的方法可以與無out和ref關鍵字的方法構成重載;但如想在out和ref間重載,編譯器將提示:不能定義僅在ref和out的上的方法重載

2 關於調用前初始值

   原則:ref作爲參數的函數在調用前,實參必須賦初始值。否則編譯器將提示:使用了未賦值的局部變量;

          out作爲參數的函數在調用前,實參可以不賦初始值。

 

3 關於在函數內,引入的參數初始值問題

   原則:在被調用函數內,out引入的參數在返回前至少賦值一次,否則編譯器將提示:使用了未賦值的out參數;

          在被調用函數內,ref引入的參數在返回前不必爲其賦初值。

總結:C#中的ref和out提供了值類型按引用進行傳遞的解決方案,當然引用類型也可以用ref和out修飾,但這樣已經失去了意義。因爲引用數據類型本來就是傳遞的引用本身而非值的拷貝。ref和out關鍵字將告訴編譯器,現在傳遞的是參數的地址而不是參數本身,這和引用類型默認的傳遞方式是一樣的。同時,編譯器不允許out和ref之間構成重載,又充分說明out和ref的區別僅是編譯器角度的,他們生成的IL代碼是一樣的。有人或許疑問,和我剛開始學習的時候一樣的疑惑:值類型在託管堆中不會分配內存,爲什麼可以按地址進行傳遞呢?值類型雖然活在線程的堆棧中,它本身代表的就是數據本身(而區別於引用數據類型本身不代表數據而是指向一個內存引用),但是值類型也有它自己的地址,即指針,現在用ref和out修飾後,傳遞的就是這個指針,所以可以實現修改後a,b的值真正的交換。這就是ref和out給我們帶來的好處。

 

 

 

out的基本用法:

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ref_out
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int a = 1;
            int b = 2;
            this.textBox1.Text = a.ToString();
            this.textBox2.Text = b.ToString();
            //swap(ref a,ref b);
            //this.textBox5.Text = a.ToString();
            //this.textBox6.Text = b.ToString();

            swap(out a, out b);
            this.textBox5.Text = a.ToString();
            this.textBox6.Text = b.ToString();
        }
        //public   void swap(ref int a,ref int b)
        //{
         
        //    int temp = a;
        //    a = b;
        //    b = temp;
        //    this.textBox3.Text = a.ToString();
        //    this.textBox4.Text = b.ToString();

        //}
        public void swap(out int a, out int b)
        {
            a = Convert.ToInt32(this.textBox1.Text);
            b =Convert.ToInt32(this.textBox2.Text);

            int temp = a;
            a = b;
            b = temp;
            this.textBox3.Text = a.ToString();
            this.textBox4.Text = b.ToString();

        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章