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:一般傳遞變數進入某個方法中接收資料;