1. 程式人生 > >C#值型別和引用型別的引數傳遞(ref,out)

C#值型別和引用型別的引數傳遞(ref,out)

C#中有兩種型別,值型別,和引用型別。在記憶體中值型別是直接儲存在記憶體的棧中的,引用型別在棧中存放一個地址,這個地址指向堆中的資料(引用型別的資料是存放在堆中的)

下面我們來看看兩種型別引數傳遞有什麼區別

先看一個例子

 /// <summary>
    ///
    /// 值型別的引數傳遞
    /// </summary>
    class ValueParms
    {
        public void Add(int a)
        {
            Console.WriteLine("Add:"+a);
        }
        
        public void Add1(ref int a)
        {
            a = a + 5;
            Console.WriteLine("Add1:"+a);
        }

        public void Add2(out int a)
        {
            a = 10;
            Console.WriteLine("Add2:"+a);
        }

    }

 class Program
    {   
        static void Main(string[] args)
        {

            int a=10;
            ValueParms valueParms = new ValueParms();

        /*@1*/    valueParms.Add(5); //5
        /*@2*/    Console.WriteLine("Main" + a);//10

    
         /*@3*/   valueParms.Add1(ref a);//15
         /*@4*/   Console.WriteLine("Main" + a);//15

       /*@5*/     valueParms.Add2(out a); //10
        /*@6*/    Console.WriteLine("Main" + a);//15

}

我們來看一下輸出的結果,第一行輸出為5,Main 方法裡面的a= 10不會隨著方法的結果而改變

第三行輸出為15 ,第四行為什麼也是15呢,是因為ref這個引數(實際是把a的地址給傳進來了),會把方法裡得到的結果(a=15)帶給Main方法裡的屬性(實參)

第五行 引數out的意思是不接收外部的資料(把外部的地址傳進來),在內部進行初始化,並把方法的結果返回帶給實參

所以第五行和第六行都是 15;

out引數用的比較多,使用帶out引數可以使我們避免大量使用try catch 語句

例子如下

string value = "ab";
            int num;
            //out傳遞也是變數的地址,內部給這個變數賦值
            if (int.TryParse(value, out num))//轉換成功就返回true,並把結果儲存在變數num中
            {
                Console.WriteLine("轉換成功:" + num);
            }
            else {
                Console.WriteLine("轉換失敗");

            }

如果我們不加out這個引數,那就要使用try catch 語句 TryParse返回 true,false兩個結果,用num去接收(相當於初始化)

並返回兩個結果 轉換成功,轉換失敗。

看完了值型別的傳遞我們來看引用型別的引數傳遞

 /// <summary>
    /// 引用型別的引數傳遞
    /// </summary>
    public class ClassParams
    {
        public void Say1(Person p)
        {
            Console.WriteLine("say1:1"+p.name); // fashi
            p = new Person();
            p.name = "xue";
            Console.WriteLine("say2:2"+p.name); //xue
        }

        public void Say2(ref Person p)
        {
            Console.WriteLine("say2:1"+p.name); //fashi
            p = new Person();
            p.name = "xue";
            Console.WriteLine("say2:2" + p.name); //xue
        }
        
        public void Say3(out Person p)
        {
            p = new Person();
            p.name = "xue";
            Console.WriteLine("say3:"+p.name);//xue
        }
    }

    public class Person
    {
        public string name;

    }

static void Main (String []args)

{

ClassParams p2 = new ClassParams();

            Person p = new Person();
            p.name = "fashi";

            p2.Say1(p);

            Console.WriteLine("Main" + p.name); //fashi

            p2.Say2(ref p);

            Console.WriteLine("Main" + p.name); //xue

            p2.Say3(out p);
            Console.WriteLine("Main" + p.name);//xue

}

  方法裡面的輸出都很容易理解,我們來講一下Main方法裡的輸出,第一呼叫沒有引數傳遞的引用型別,Main裡面輸出fashi

第二種帶引數的ref 為什麼輸出的是xue,因為呼叫帶ref引數的方法把  p指向的xue的地址賦值給了p指向fashi的地址,所以Main裡面輸出的是fashi

第三種帶引數的out的不接收方法裡的引數,並把自己的初始化的地址賦值給了p指向的fashi的地址,所以Main裡面輸出的是xue

我們現在來總結一下

 *ref,out:傳遞值型別的變數的地址;

 * ref:傳遞過程中保留初始值;
 * out:傳遞過程中不保留初始值;
 * ref:一般需要把外部的值傳入到方法中使用並進行修改;
 * out:一般傳遞變數進入某個方法中接收資料;