c#入參使用引用類型為啥要加ref?

摘一段來自官網的說明 :方法的參數列表中使用 ref 關鍵字時,它指示參數按引用傳遞,而非按值傳遞。 ref 關鍵字讓形參成為實參的別名,這必須是變數。 換而言之,對形參執行的任何操作都是對實參執行的。
大體意思就是將實慘的引用作為參數傳遞,如果入參不加ref修飾,本身傳遞的是實參的值到方法中。

那什麼是值,什麼是引用?大體可以理解為堆棧的區別,在.net中大多數實例存在於託管堆棧中。struct,int32,int64,double,enum等數據類型聲明的實例存在棧中,而string,class,delegate等存在於堆中。前者一般稱為值類型,後者則叫做引用類型,那麼引用類型和值類型在記憶體中的大概圖如下:

可以看到引用類型被存儲的時候真正內容存在堆中,而在stack中存儲了一個堆中地址的引用指向堆中真正內容。

ref修飾入參的常用場景

當我們希望一個已有返回值的方法能夠修改我一個外部基本類型的值的時候,我們可以將該參數加上ref關鍵字作為入參。具體原理其實就是將棧中具體的值替換為了棧的引用,說白了就是地址,幻想一下本來你高考作弊只是後台改了一下自己成績和學霸一樣,學霸的人生沒有受到影響,現在你乾脆把學霸的試卷改成了自己名字,學霸直接被你影響只能進廠打螺絲了,當然這個比喻不是很貼切,大家理解到意思就行。
那麼為什麼引用類型的入參我們不需要要添加ref?因為從上圖我們能看出來棧中存儲的本來就是引用類型的地址,所以引用類型不需要添加ref關鍵字,當你在方法內部修改了入參的一些屬性值,外面的實慘依舊會受到影響。

引用類型添加ref的作用是啥?

我們在實際開發中還是能夠碰到一些引用類型添加ref的場景。其實道理也是一樣的,就是將引用類型的棧的地址傳遞到了方法中,那麼和不添加有啥區別?我們來看看下面的程式碼:

Student student1 = new Student("Jack");
Student student2 = new Student("Lucy");
WithoutRef(student1);
WithRef(ref student2);
Console.WriteLine($"不帶ref的方法---{student1.Name}");
Console.WriteLine($"帶ref的方法---{student2.Name}");

Console.Read();
//不帶ref的方法
static void WithoutRef(Student stu) 
{
    stu.Name = "Bruce";
}

//不帶ref的方法
static void WithRef(ref Student stu)
{
    stu.Name = "Bruce";
}

public class Student
{
	public Student(string name)
	{
        Name = name;

    }
    public string Name { get; set; }
}


可以看到結果一樣的,兩個實例的名字都變了

那我們再看看下面的程式碼:

Student student1 = new Student("Jack");
Student student2 = new Student("Lucy");
WithoutRef(student1);
WithRef(ref student2);
Console.WriteLine($"不帶ref的方法---{student1.Name}");
Console.WriteLine($"帶ref的方法---{student2.Name}");

Console.Read();
//不帶ref的方法
static void WithoutRef(Student stu) 
{
    stu = new Student("Bruce");
}

//不帶ref的方法
static void WithRef(ref Student stu)
{
    stu = new Student("Bruce");
}

public class Student
{
	public Student(string name)
	{
        Name = name;

    }
    public string Name { get; set; }
}


我們發現第一個不帶ref的方法名字沒有發生變化。
程式碼的變化就是第二個例子重新new了一下,我們根據上面的原理分析就能知道為啥。由於ref將引用類型的棧地址傳遞過去了,new關鍵字大體就是在堆中開闢一個新空間,然後將空間地址存儲到棧中,由於ref將棧地址傳遞過來了,所以就將棧中的存儲地址替換為了新開闢的堆地址了,而不帶ref的引用類型本身傳遞的只是堆中地址的引用,所以new關鍵字等於說將形參重新開闢空間和分配了,和實參已經不是同一個地方了。

總結

ref傳遞的是棧地址
不帶ref傳遞的是棧中存儲的值,可能是一個值,也可能是一個地址引用。

Tags: