《Effective C 》之減少裝箱和拆箱

2021-04-13 11:30:16 字數 3190 閱讀 6418

為了便於文章的開展,首先介紹裝箱(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.宣告式 是告訴編譯器某個東西的名稱和型別,而略...