C#類型轉化的五點心得
對于這種C#類型轉化我們通常用兩種選擇:使用as操作符,或者使用強制轉型。當然還有一個做法是先用is測試轉換是否可行,然后再用as或者強制轉換。
本文主要給大家提醒一些使用C#類型轉化時需要注意的地方,或許你更想弄清楚as和強制轉換的區別和使用場景,不用著急,看完,本文你自己就知道答案了,那么上面提到的兩種C#類型轉化我們需要注意哪些呢?
1、as和is操作符都不執行任何用戶自定義的轉換。
2、對于強制轉換,引用為空將會轉換出錯。
3、強制轉換任意類型和自定義轉換兩種情況的IL代碼展示有區別。
4、用戶自定義轉換只作用于對象的編譯時類型。
5、as操作符不能應用于值類型。
6、foreach循環語句中使用強制類型轉型。
下面我們逐一進行介紹說明:
1,我們先來看錯誤代碼示例:
- classA
- {
- }
- classC
- {
- publicstaticimplicitoperatorA(Ct)
- {
- returnnewA();
- }
- }
- classProgram
- {
- staticvoidMain(string[]args)
- {
- objecto=Factory.GetObject();
- //o為一個C類型:
- Aa=oasA;//轉型失敗,o的類型不是A
- }
- }
代碼已經很明顯,我們不可以因為定義了C到A的強制轉換,就使用as,對應用戶自定義轉換,我們只可以使用(A)o轉換,其實自定義轉換和自定義操作=、+、-、"、%等操作符是一樣的機理,這樣你就明白為什么用as不可以使用自定義轉換了。
2,還是看一個錯誤示例:
- classA
- {
- }
- classB:A
- {
- }
- classProgram
- {
- staticvoidMain(string[]args)
- {
- Bb;
- Aa=(A)b;
- }
- }
- 問題不用多言,as可以解決這個問題。
- 3,看一個示例:
- classA
- {
- }
- classC
- {
- publicstaticimplicitoperatorA(Ct)
- {
- returnnewA();
- }
- }
- classB:A
- {
- }
- classProgram
- {
- staticvoidMain(string[]args)
- {
- Aa=newB();
- Bb=(B)a;
- Cc=newC();
- a=(A)c;
- }
- }
- 對于Bb=(B)a;的IL代碼如下:
- IL_0008:castclassConsoleApplication1.B
- 對于a=(A)c;的IL代碼如下:
- IL_0015:callclassConsoleApplication1.AConsoleApplication1.C::op_Implicit(classConsoleApplication1.C)
- 區別大家已經看到了,要說真正認識區別那么我們要繼續談“4、用戶自定義轉換只作用于對象的編譯時類型。"
- 4,看一個示例:
- classA
- {
- }
- classC
- {
- publicstaticimplicitoperatorA(Ct)
- {
- returnnewA();
- }
- }
- classB:A
- {
- }
- classProgram
- {
- staticvoidMain(string[]args)
- {
- Aa=newB();
- Bb=(B)a;
- objectc=newC();
- a=(A)c;//編譯通過,運行失敗!
- }
- }
也許你從來沒想過a=(A)c;編譯會成功,運行會出錯在這句,畢竟我們有一個顯示類型C到類型A的轉換操作,我們看看IL代碼即可找到答案,
- .methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged
- {
- .entrypoint
- //Codesize28(0x1c)
- .maxstack1
- .localsinit([0]classConsoleApplication1.Aa,
- [1]classConsoleApplication1.Bb,
- [2]objectc)
- IL_0000:nop
- IL_0001:newobjinstancevoidConsoleApplication1.B::.ctor()
- IL_0006:stloc.0
- IL_0007:ldloc.0
- IL_0008:castclassConsoleApplication1.B
- IL_000d:stloc.1
- IL_000e:newobjinstancevoidConsoleApplication1.C::.ctor()
- IL_0013:stloc.2
- IL_0014:ldloc.2
- IL_0015:castclassConsoleApplication1.A
- IL_001a:stloc.0
- IL_001b:ret
- }//endofmethodProgram::Main
大家注意看IL_0015:castclassConsoleApplication1.A這句,這句說明自定義轉換在編譯時刻進行,也許你要問為什么不是第3條的:
IL_0015:callclassConsoleApplication1.AConsoleApplication1.C::op_Implicit(classConsoleApplication1.C)
因為objectc,c被定義為Object類型,那么強制轉換在編譯時刻去Object找是否存在自定義轉換操作(注意,()轉型時編譯器優先考慮自定義轉換,找不到才進行castclass),當然Object沒有自定義轉換為A的操作,那么就使用普通的強制轉換castclass。好了現在我們知道了用戶自定義轉換只作用于對象的編譯時類型,而普通的Bb=(B)a;強制轉換可以作用到運行時刻。那么上面的錯誤如何去掉呢?對應代碼修改為:
- objectc=newC();
- Ccc=casC;
- a=(A)cc;
現在運行正常通過,好了我們再次查看IL代碼
IL_001c:callclassConsoleApplication1.AConsoleApplication1.C::op_Implicit(classConsoleApplication1.C)
到這大家對3和4點的認識應該很清楚了吧。
5、as操作符不能應用于值類型————省略!,鑒于這個點很簡單,本人就不提供示例了,大家有興趣可以自己試驗下。
6、看代碼示例:(以下代碼摘自《EffectiveC#中文版改善C#程序的50中方法》——23頁)
- publicvoidUseCollection(IEnumerabletheCollection)
- {
- foreach(MyTypetintheCollection)
- t.DoStuff();
- }
- //上面代碼等同于:
- publicvoidUseCollection(IEnumerabletheCollection)
- {
- IEnumeratorit=hteCollection.GetEnumerator();
- while(it.MoveNext())
- {
- MyTypet=(MyType)it.Current;
- t.DoStuff();
- }
- }
通過查看IL代碼我們可以確認foreach語句的轉換是使用的強制轉換操作,那么為什么呢?之所以使用強制轉型,是因為foreach語句需要同時支持值類型和引用類型,這側面說明我們的第5點as不支持值類型。
好了,5點C#類型轉化說明已經解釋完了,你現在還想問as和強制轉換的區別和使用場景么?,歡迎提出批評、指正錯誤。
【編輯推薦】