換 系統(tǒng)精講(新手友好,詳盡版))
前置回顧C(jī)# 是強(qiáng)類型靜態(tài)語(yǔ)言。變量一旦聲明類型就固定。 類型轉(zhuǎn)換 將一個(gè)類型的數(shù)據(jù)變成另一個(gè)類型的數(shù)據(jù)。核心總綱隱式轉(zhuǎn)換編譯器自動(dòng)幫你轉(zhuǎn)安全、不會(huì)丟數(shù)據(jù)無(wú)需寫(xiě)任何轉(zhuǎn)換代碼。顯式強(qiáng)制轉(zhuǎn)換強(qiáng)制轉(zhuǎn)型你手動(dòng)告訴編譯器去轉(zhuǎn)換有可能丟失數(shù)據(jù)、溢出必須寫(xiě)(目標(biāo)類型)。Convert 工具類轉(zhuǎn)換跨類型轉(zhuǎn)換神器尤其適合【字符串 ? 基礎(chǔ)類型】轉(zhuǎn)換失敗直接拋異常。TryParse 安全解析推薦生產(chǎn)代碼使用嘗試解析失敗不崩潰返回 bool 標(biāo)記成功與否。裝箱 拆箱值類型 ? object 引用類型之間的特殊轉(zhuǎn)換伴隨內(nèi)存操作。as 與 is 運(yùn)算符引用類型安全轉(zhuǎn)換避免強(qiáng)制轉(zhuǎn)換拋異常。學(xué)習(xí)思路逐個(gè)學(xué)習(xí)每種轉(zhuǎn)換方式 → 看懂內(nèi)存機(jī)理 → 對(duì)比適用場(chǎng)景 → 識(shí)別坑點(diǎn) → 動(dòng)手敲示例。1. 隱式類型轉(zhuǎn)換Implicit Conversion一句話定義由編譯器自動(dòng)完成不需要寫(xiě)任何轉(zhuǎn)換代碼只有保證不會(huì)丟失信息時(shí)編譯器才允許隱式轉(zhuǎn)換。規(guī)則小范圍類型 → 大范圍類型安全擴(kuò)容。整數(shù)從小到大順序方便記憶sbyte→short→int→long→float→double為什么這個(gè)順序 把小容器的數(shù)據(jù)放進(jìn)更大的容器數(shù)據(jù)完整保留不會(huì)丟失。示例 1整數(shù)隱式轉(zhuǎn)換using System; namespace ImplicitDemo { class Program { static void Main(string[] args) { int numInt 100; long numLong numInt; // ? 隱式轉(zhuǎn)換 int → long自動(dòng)完成 double numDouble numInt; // ? int → double 隱式轉(zhuǎn)換 Console.WriteLine($numInt{numInt}, numLong{numLong}, numDouble{numDouble}); } } }機(jī)理拆解numInt棧內(nèi)存是 int4 字節(jié)存放 100賦值給numLonglong 8 字節(jié)編譯器自動(dòng)把 100 復(fù)制一份放到 8 字節(jié)空間數(shù)據(jù)完整無(wú)損long 容量比 int 大不存在溢出、丟失數(shù)據(jù)風(fēng)險(xiǎn)編譯器允許自動(dòng)轉(zhuǎn)換。? 反向不允許大范圍 → 小范圍不能隱式轉(zhuǎn)換long big 99999; int small big; //編譯報(bào)錯(cuò)long范圍大于int可能數(shù)據(jù)丟失編譯器禁止隱式轉(zhuǎn)換示例 2char 隱式轉(zhuǎn) intchar 底層存儲(chǔ) Unicode 編碼數(shù)字可以隱式轉(zhuǎn)為 intchar ch A; int code ch; //隱式轉(zhuǎn)換 A 的Unicode碼65存入code Console.WriteLine(code); //輸出65但是 int不能隱式轉(zhuǎn) char因?yàn)?int 范圍太大可能超出字符編碼范圍。示例 3隱式轉(zhuǎn)換適用場(chǎng)景方法傳參時(shí)自動(dòng)隱式轉(zhuǎn)換static void PrintDouble(double d) { Console.WriteLine(d); } static void Main(string[] args) { int a 10; PrintDouble(a); //int自動(dòng)隱式轉(zhuǎn)為double }? 隱式轉(zhuǎn)換特點(diǎn)總結(jié)自動(dòng)執(zhí)行代碼干凈無(wú)損不會(huì)截?cái)?、丟失數(shù)據(jù)只能小范圍→大范圍編譯期檢查不通過(guò)直接報(bào)錯(cuò)。2. 顯式強(qiáng)制轉(zhuǎn)換Explicit Cast強(qiáng)制轉(zhuǎn)型一句話程序員手動(dòng)強(qiáng)制告訴編譯器執(zhí)行類型轉(zhuǎn)換。你主動(dòng)承擔(dān)數(shù)據(jù)丟失、溢出風(fēng)險(xiǎn)。語(yǔ)法格式目標(biāo)類型 變量名 (目標(biāo)類型)源變量;適用場(chǎng)景大范圍類型 → 小范圍類型double→int 等。示例 1double 強(qiáng)制轉(zhuǎn)為 int小數(shù)截?cái)嗖皇撬纳嵛迦胄率执罂觰sing System; namespace CastDemo { class Program { static void Main(string[] args) { double d1 3.99; int num1 (int)d1; //強(qiáng)制轉(zhuǎn)換 double → int Console.WriteLine(num1); //輸出3小數(shù)直接丟掉不會(huì)四舍五入 double d2 -2.99; int num2 (int)d2; Console.WriteLine(num2); //輸出 -2 } } }機(jī)理強(qiáng)制轉(zhuǎn)型(int)作用直接砍掉小數(shù)部分只保留整數(shù)部分不是數(shù)學(xué)上的四舍五入。如果想要四舍五入要用Math.Round()而不是強(qiáng)制轉(zhuǎn)型。double d 3.99; int res (int)Math.Round(d); Console.WriteLine(res); //4示例 2long 強(qiáng)制轉(zhuǎn) int溢出風(fēng)險(xiǎn)long bigNum 3000000000; //超過(guò)int最大值2147483647 int smallNum (int)bigNum; Console.WriteLine(smallNum); //得到一個(gè)錯(cuò)誤負(fù)數(shù)發(fā)生溢出機(jī)理long 8 字節(jié)數(shù)據(jù)強(qiáng)行塞進(jìn) 4 字節(jié) int 空間超出部分直接截?cái)嘟Y(jié)果錯(cuò)亂。生產(chǎn)環(huán)境需要溢出檢查可以使用checked關(guān)鍵字開(kāi)啟溢出檢測(cè)溢出會(huì)拋異常。checked { long bigNum 3000000000; int smallNum (int)bigNum; //溢出運(yùn)行時(shí)報(bào)錯(cuò) }示例 3int 強(qiáng)制轉(zhuǎn) charint code 65; char ch (char)code; Console.WriteLine(ch); //輸出 A?? 強(qiáng)制轉(zhuǎn)換的局限(int)這種強(qiáng)制轉(zhuǎn)型不能直接把字符串轉(zhuǎn)數(shù)字string str 123; int num (int)str; //編譯報(bào)錯(cuò)string和int完全不兼容不能用這種強(qiáng)制轉(zhuǎn)型字符串轉(zhuǎn)數(shù)字要用 Convert / Parse這是新手高頻錯(cuò)誤。? 強(qiáng)制轉(zhuǎn)換適用場(chǎng)景數(shù)字類型之間互相轉(zhuǎn)換明確知道數(shù)據(jù)不會(huì)溢出。3. Convert 類轉(zhuǎn)換Convert是.NET 內(nèi)置工具類提供大量靜態(tài)方法用于跨類型轉(zhuǎn)換。核心特點(diǎn)可以把字符串、bool、數(shù)字互相轉(zhuǎn)換轉(zhuǎn)換失敗直接拋出異常程序崩潰。常用方法Convert.ToInt32()→ 轉(zhuǎn)為 intConvert.ToDouble()→ 轉(zhuǎn)為 doubleConvert.ToDecimal()→ 轉(zhuǎn)為 decimal金額Convert.ToString()→ 任意類型轉(zhuǎn)為字符串Convert.ToBoolean()→ 轉(zhuǎn)為 bool示例 1字符串轉(zhuǎn)數(shù)字控制臺(tái)程序最常用using System; namespace ConvertDemo { class Program { static void Main(string[] args) { Console.Write(請(qǐng)輸入數(shù)字); string input Console.ReadLine(); int num Convert.ToInt32(input); Console.WriteLine(數(shù)字10 (num 10)); } } }機(jī)理Convert.ToInt32內(nèi)部會(huì)解析傳入字符串嘗試識(shí)別數(shù)字。 ? 用戶輸入100→ 成功轉(zhuǎn)為 100 ? 用戶輸入abc→拋出 FormatException 異常程序直接崩潰。示例 2數(shù)字轉(zhuǎn)字符串int a 99; string s Convert.ToString(a); Console.WriteLine(s);示例 3Convert 處理小數(shù)轉(zhuǎn) int和強(qiáng)制轉(zhuǎn)型不一樣重點(diǎn)對(duì)比(int)直接截?cái)嘈?shù)Convert.ToInt32 會(huì)四舍五入double d 3.5; int r1 (int)d; //3直接截?cái)?int r2 Convert.ToInt32(d); //4四舍五入 Console.WriteLine($r1{r1}, r2{r2});這是非常重要的區(qū)別很多新手踩坑。? Convert 適用場(chǎng)景快速簡(jiǎn)單轉(zhuǎn)換確定輸入一定合法不會(huì)出現(xiàn)錯(cuò)誤格式。如果輸入來(lái)源是用戶不推薦直接用 Convert。4. Parse / TryParse字符串解析?生產(chǎn)代碼首選Parse字符串解析成對(duì)應(yīng)類型失敗拋異常。TryParse安全嘗試解析失敗不崩潰返回 bool 表示是否成功。適用場(chǎng)景專門把字符串解析成基礎(chǔ)值類型int、double、decimal 等。4.1 Parse 示例string str 123; int num int.Parse(str); Console.WriteLine(num); //int num2 int.Parse(abc); //運(yùn)行報(bào)錯(cuò)解析失敗機(jī)理int.Parse只負(fù)責(zé)解析字符串文本為整數(shù)輸入非數(shù)字字符串直接異常。和 Convert.ToInt32 細(xì)微差別Parse只能接收字符串Convert 可以接收多種類型。4.2 TryParse重點(diǎn)寫(xiě)項(xiàng)目?jī)?yōu)先用這個(gè)語(yǔ)法模板bool 結(jié)果 類型.TryParse(待解析字符串, out 輸出變量);返回 booltrue 解析成功false 解析失敗out輸出參數(shù)解析成功解析結(jié)果放入變量失敗變量默認(rèn)值 0。using System; namespace TryParseDemo { class Program { static void Main(string[] args) { Console.Write(請(qǐng)輸入年齡); string input Console.ReadLine(); int age; bool parseSuccess int.TryParse(input, out age); if (parseSuccess) { Console.WriteLine($解析成功你的年齡{age}); } else { Console.WriteLine(輸入格式錯(cuò)誤不是合法數(shù)字); } } } }機(jī)理拆解用戶輸入文本存入字符串 inputint.TryParse嘗試從頭到尾解析這個(gè)字符串如果全部字符都是合法數(shù)字返回 true把解析后的數(shù)字寫(xiě)入 age 變量如果有字母、符號(hào)返回 falseage 賦值默認(rèn) 0不會(huì)拋出異常程序不會(huì)崩潰。TryParse 是處理用戶輸入的最佳實(shí)踐做控制臺(tái)、網(wǎng)頁(yè)、桌面程序用戶輸入數(shù)字一律優(yōu)先 TryParse。拓展double.TryParse、decimal.TryParse、bool.TryParse 用法完全一致。5. 裝箱與拆箱特殊類型轉(zhuǎn)換值類型 ? objectobject 是 C# 所有類型的根父類。任何值類型都可以轉(zhuǎn)為 object這個(gè)過(guò)程叫裝箱object 轉(zhuǎn)回原來(lái)的值類型叫拆箱。 本質(zhì)跨越棧內(nèi)存和堆內(nèi)存的轉(zhuǎn)換有性能開(kāi)銷。示例裝箱 拆箱using System; namespace BoxDemo { class Program { static void Main(string[] args) { int num 10; //棧內(nèi)存值類型 object obj num; //【裝箱】 Console.WriteLine(obj); int num2 (int)obj; //【拆箱】強(qiáng)制轉(zhuǎn)換取出值 Console.WriteLine(num2); } } }機(jī)理非常關(guān)鍵int num 10;棧開(kāi)辟 4 字節(jié)保存 10object obj num;裝箱在堆內(nèi)存開(kāi)辟一塊內(nèi)存把棧里的 10 復(fù)制一份放到堆棧上 obj 變量保存堆中對(duì)象的地址裝箱 在堆包裝值類型變成引用對(duì)象。int num2 (int)obj;拆箱強(qiáng)制轉(zhuǎn)型檢查堆內(nèi) obj 里面存放的是不是 int如果類型匹配把堆里面的值拷貝回棧 num2 變量如果類型不匹配直接拋異常。? 拆箱必須拆回原本類型不能亂拆int num 10; object obj num; double d (double)obj; //運(yùn)行報(bào)錯(cuò)obj里面裝的是int不能直接拆成double注意拆箱必須先拆回原類型再做其他轉(zhuǎn)換。double d (double)(int)obj; //先拆箱為int再隱式轉(zhuǎn)為double? 裝箱拆箱使用場(chǎng)景老版本 API 接收 object 參數(shù)高頻循環(huán)盡量避免裝箱拆箱會(huì)帶來(lái) GC 壓力。6. is 運(yùn)算符 判斷類型as 安全引用類型轉(zhuǎn)換is判斷對(duì)象是不是某個(gè)類型返回 boolas嘗試把引用類型轉(zhuǎn)換轉(zhuǎn)換失敗不會(huì)拋異常得到 null。 ?? is 和 as只用于引用類型class、string不能用于值類型。示例 isobject obj Hello; if(obj is string) { Console.WriteLine(obj是字符串類型); }示例 asobject obj 測(cè)試文本; string str obj as string; if(str ! null) { Console.WriteLine(str); } //as失敗時(shí)strnull不會(huì)報(bào)錯(cuò)對(duì)比強(qiáng)制轉(zhuǎn)型object obj 10; string s (string)obj; //強(qiáng)制轉(zhuǎn)換運(yùn)行時(shí)報(bào)錯(cuò) string s2 obj as string; //as轉(zhuǎn)換失敗得到null不報(bào)錯(cuò)7. 轉(zhuǎn)換方式對(duì)比總表轉(zhuǎn)換方式語(yǔ)法適用場(chǎng)景失敗表現(xiàn)隱式轉(zhuǎn)換直接賦值小范圍值類型 → 大范圍值類型編譯報(bào)錯(cuò)不允許轉(zhuǎn)換強(qiáng)制轉(zhuǎn)型(目標(biāo)類型)變量數(shù)字之間、裝箱拆箱溢出 / 類型不匹配運(yùn)行報(bào)錯(cuò)ConvertConvert.ToXXX()多類型互轉(zhuǎn)字符串轉(zhuǎn)數(shù)字失敗拋異常Parse類型.Parse (str)字符串→基礎(chǔ)類型失敗拋異常TryParse類型.TryParse (str,out var)用戶輸入字符串解析返回 false程序不崩潰asobj as 類型引用類型安全轉(zhuǎn)換失敗返回 null8. 新手高頻坑匯總(int)3.99直接截?cái)嘈?shù)不是四舍五入想要四舍五入用Convert.ToInt32或者M(jìn)ath.Round不能用(int)123把字符串轉(zhuǎn)數(shù)字字符串和數(shù)字不兼容要用 Parse / Convert / TryParse拆箱必須拆回原始類型不能直接拆成別的類型隱式轉(zhuǎn)換只能從小→大大→小必須強(qiáng)制轉(zhuǎn)換只要是用戶輸入、外部讀取的字符串一律優(yōu)先 TryParse不要直接 Parse 和 Convert裝箱拆箱會(huì)產(chǎn)生堆對(duì)象大量循環(huán)中盡量減少as不能用于值類型is可以用于值類型判斷。