為了便於文章的開展,首先介紹裝箱(boxing)和拆箱(unboxing) 這兩個名詞。.net的型別分為兩種,一種是值型別,另一種是引用型別。這兩個型別的本質區別,值型別資料是分配在棧中,而引用型別資料分配在堆上。那麼 如果要把乙個值型別資料放到堆上,就需要裝箱操作;反之,把乙個放在堆上的值型別資料取出來,則需要進行拆箱操作。
例如,對於如下簡單的裝箱和拆箱操作語句。
int i = 123;
object obj = i;//boxing
if( obj is int )
int j = (int) obj;//unboxing
為了,更好的詮釋裝箱和拆箱操作,我借用msdn關於「boxing」的解釋圖,具體如下。
明白了這兩名詞的意思,現在說說為什麼要減少裝箱和拆箱操作。
原因有兩個,主要是關於效率:乙個就是對於堆的操作效率比較低;另乙個就是對於堆上分配的記憶體資源,需要gc來**,從而降低程式效率。
考慮到這兩點因素,那麼需要在程式中減少裝箱和拆箱操作。
如何減少呢,涉及到這兩個操作比較多的是,格式化輸出操作,例如:string.format,console.writeline之類的語句。
例如:console.writeline( "number list:, , ",1,2,3 );
對於「1,2,3」來說,相當於前面的「123」一樣,需要經過裝箱和拆箱兩個操作。那麼如何避免呢,其實只要向writeline傳遞引用型別資料即可,也就是按照如下的方式。
console.writeline( "number list:, , ", 1.tostring(),2.tostring(),3.tostring() );
由於「1.tostring()」的結果是string型別,屬於引用型別,因此不牽扯裝箱和拆箱操作。
其次,牽扯到裝箱和拆箱操作比較多的就是在集合中,例如:arraylist或者hashtable之類。
把值型別資料放到集合中,可能會出現潛在錯誤。例如:
public struct person
set}
public person( string personname )
public override string tostring()
}// using the person in a collection
arraylist arrpersons = new arraylist();
person p = new person( "oldname" );
arrpersons.add( p );
// try to change the name
p = ( person ) arrpersons[0] ;
p.name = "newname";
debug.writeline( ( (person ) arrpersons[0] ).name );//it's "oldname"
這個問題其實在前面的文章中已經講過了。有人可能會說,是否可以按照如下的方式去修改呢。
( (person ) arrpersons[0] ).name = "newname";//can't be compiled
很不幸,如上操作不能通過編譯。為什麼呢,對於「( (person ) arrpersons[0] )」來說,是系統用乙個臨時變數來接收拆箱後的值型別資料,那麼由於值型別是分配在棧上,那麼操作是對實體操作,可是系統不允許對乙個臨時值型別資料進行修改操作。
// using the person in a collection
arraylist arrpersons = new arraylist();
person p = new person( "oldname" );
arrpersons.add( p );
// try to change the name
p = ( person ) arrpersons[0] ;
p.name = "newname";
arrpersons.removeat( 0 );//remove old data first
arrpersons.insert( 0, p );//add new data
debug.writeline( ( (person ) arrpersons[0] ).name );//it's "newname"
其實,這樣操作會產生過多裝箱和拆箱操作。那麼更好的方法,可以通過介面來完成,從而減少裝箱和拆箱操作。對於這個例子的介面實現應該如下。
public inte***ce ipersonname
}public struct person:ipersonname
set}
public person( string personname )
public override string tostring()
}// using the person in a collection
arraylist arrpersons = new arraylist();
person p = new person( "oldname" );
arrpersons.add( p );
// change the name
( (ipersonname)arrpersons[0] ).name = "newname";
debug.writeline( ( (person ) arrpersons[0] ).name );//it's "newname"
很多人就問,為什麼值型別不能修改,即
( (person ) arrpersons[0] ).name = "newname";//can't be compiled
而如上的介面型別就能修改呢,即
( (ipersonname)arrpersons[0] ).name = "newname";
這是由於產生的臨時變數的型別不同,前者已經在前面進行說明了,後者由於產生的臨時變數的型別為ipersonname,屬於引用型別,那麼相當於臨時 變數就是原物件的引用,那麼對於它的修改會直接修改到原物件,因此是可以的。可以說這裡的不同本身在於產生臨時物件的型別不同,從而造成本質的區別。
通過介面來改寫,這樣就減少了裝箱和拆箱操作,同時也保證了修改的正確性。不過要注意的是,這裡介面對於的是引用型別,如果介面訪問的或者返回的是值型別,那麼用介面雖說能實現了,但是對於裝箱和拆箱操作來說,並沒有減少。
對於裝箱和拆箱操作來說,基本上就講完了,只要記住頻繁裝箱和拆箱操作會降低程式效率,因此在編寫的時候要盡量避免。
EffectiveC 17 裝箱和拆箱的最小化
1.如下這段 會經歷裝箱和拆箱。例如25會先裝箱成object後傳遞給writeline方法 一次拷貝 在方法內部又 經歷拆箱成int 第二次拷貝 後然後呼叫tostring console.writeline a few numbers 25,32,50 所以建議這種寫法 只經歷一次裝箱 cons...
找工作之Effective C
1 盡量以const,enum,inline替換 define 2 const出現在星號左邊,表明指物是常量 出現在星號右邊,指標是常量。3 mutable修辭可以突破const限制,在被const修辭的函式裡面也能被修改 4 運用const成員函式實現non const版本可以避免 重複 5 co...
Effective C 之導讀部分
宣告 文中內容收集整理自 effective c 本內容在作者現有能力的基礎上有所刪減,另加入部分作者自己的理解,有紕漏之處敬請指正。目錄 1.術語 宣告和定義 初始化值傳遞和const引用傳遞 2.命名習慣 3.關於執行緒 4.tr1和boost 1.宣告式 是告訴編譯器某個東西的名稱和型別,而略...