值類(lèi)型和引用類(lèi)型
工作兩年多了,,雖然一直用.NET,卻一直沒(méi)有系統(tǒng)的看過(guò)C#方面的專(zhuān)業(yè)書(shū)籍,用到哪里就看到哪里,,有時(shí)候覺(jué)得很亂,,不了解C#里面到底有哪些內(nèi)容,回答別人的問(wèn)題時(shí),,也不是很有信心,,就決定把.NET的知識(shí)系統(tǒng)的看一下,順便把基礎(chǔ)和重要的知識(shí)記下來(lái),。
值類(lèi)型和引用類(lèi)型是.NET中最基本的知識(shí),,雖然是最基本的,但在最近看書(shū)的過(guò)程中,,才發(fā)現(xiàn)自己有很多不了解的,。
1) 內(nèi)存分配上的區(qū)別.
值類(lèi)型分配在棧上的,引用類(lèi)型分配在托管堆中,。
IList list; //此時(shí)CLR僅僅在棧上創(chuàng)建了一個(gè)引用,
//但是并沒(méi)有指向任何對(duì)象實(shí)例,,如果此時(shí)編譯是通不過(guò)的.
list = new ArrayList();//此時(shí)在托管堆上創(chuàng)建了一個(gè)ArrayList的實(shí)例,
//并且使list指向ArrayList的實(shí)例在托管堆中的地址.
2) 作為參數(shù)傳給方法的區(qū)別.
值類(lèi)型是以拷貝值的方式進(jìn)行傳遞,。在傳參數(shù)時(shí),,CLR在棧上創(chuàng)建了一個(gè)新的和原來(lái)一模一樣的值類(lèi)型。
引用類(lèi)型是拷貝引用的方式進(jìn)行傳遞.在傳參數(shù)時(shí),,CLR在棧上創(chuàng)建了一個(gè)新的引用,,但他和原來(lái)的引用指向同一個(gè)托管堆中的實(shí)例.
public void changeAge(int maxAge); // 此時(shí)是值拷貝
IList list = new ArrayList();
public void AddList(IList list, object obj); // 此時(shí)是引用拷貝
當(dāng)然.NET中使用ref, out 關(guān)鍵字也可以示值類(lèi)型也以引用類(lèi)型的方式傳遞參數(shù).
ref 在傳遞給方法時(shí)必須先被初始化.
out 在傳遞前不必先初始化,但在方法中必須顯式的給參數(shù)付值.
值類(lèi)型和引用類(lèi)型在使用ref(out)作為參數(shù)傳遞時(shí)的區(qū)別.
public void changeAge(int valAge); // 此時(shí)進(jìn)行了值拷貝
{
valAge = 20; // 此時(shí)maxAge = 10; valAge = 20;
}
public void changeAge(ref int refAge)// 此時(shí)創(chuàng)建一個(gè)指向maxAge的引用
{
refAge = 20; // 此時(shí)maxAge = 20; valAge = 20;
}
IList list = new ArrayList();
public void AddList(IList lst, object obj) // 此時(shí)創(chuàng)建一個(gè)新的引用lst和list指向同一個(gè)實(shí)例,;
{
lst.Add(obj); // lst和list里有相同的元素.
lst = new ArrayList(); // lst是一個(gè)指向新的實(shí)例的引用(0個(gè)元素),,
//list仍然指向原來(lái)的實(shí)例(一個(gè)元素).
}
public void AddList(ref IList lst, object obj) // 此時(shí)list將自己傳給方法.
{
lst.Add(obj); // lst和list里有相同的元素(一個(gè)元素).
lst = new ArrayList(); // lst和list都指向了新的元素(0個(gè)元素).
}
在使用ref和out的時(shí)候,,要注意下面的情況:
{
public static void Main(string[] args)
{
string str = string.Empty;
Method(str);
Method(ref str); //這行編譯是通不過(guò)的.無(wú)法將ref string轉(zhuǎn)為ref Object.
// 你看這個(gè)方法的實(shí)現(xiàn),obj被指向了另一個(gè)實(shí)例,
//而這個(gè)實(shí)例類(lèi)型不一定是string(而是OtherClass),,這是非常危險(xiǎn)的,,
// 所以編譯是不會(huì)通過(guò)的.
}
public static void Method(Object obj)
{
}
public static void Method(ref Object obj)
{
obj = new OtherClass();
}
public static void Method(out Object obj) // 這個(gè)方法編譯是通不過(guò)的,
//因?yàn)橹剌d時(shí)不能指通過(guò)ref和out來(lái)區(qū)別.
{
}
}
3) 他們之間的轉(zhuǎn)換.
值類(lèi)型和引用類(lèi)型之間的轉(zhuǎn)換稱(chēng)之為裝箱和拆箱,具體概念就不說(shuō)了,在性能方面裝箱耗費(fèi)較多的系統(tǒng)資源.
{
int Multi();
}
public Struct Point : IMulti
{
public int x;
public int y;
public override ToString()
{
Console.WriteLine(x + " : " + y);
}
public int Multi()
{
return x * y;
}
}
public void static Main(string[] args)
{
Point p = new Point();
p.x = 10;
p.y = 10;
p.ToString(); //此時(shí)并沒(méi)有進(jìn)行裝箱(CLR會(huì)自動(dòng)調(diào)用Point的方法).
p.Multi(); //此時(shí)并沒(méi)有進(jìn)行裝箱(CLR會(huì)自動(dòng)調(diào)用Point的方法).
Type t = p.GetType(); //此時(shí)進(jìn)行了裝箱.因?yàn)檎{(diào)用了基類(lèi)的GetType方法.
IMulti m = (IMulti)p; //此時(shí)進(jìn)行了裝箱.因?yàn)榻杩谑且妙?lèi)型.
m.Multi();
}
4) 還有和他們有關(guān)的,,我聯(lián)想不到了.閣下可以補(bǔ)充.