官术网_书友最值得收藏!

5.1 類型轉換

本書前面說過,無論是什么類型,所有數據都是一系列的位,即一系列0和1。變量的含義是通過解釋這些數據的方式來確定的。最簡單的示例是char類型,這種類型用一個數字表示Unicode字符集中的一個字符。實際上,這個數字與ushort的存儲方式完全相同——它們都存儲0和65535之間的數字。

但一般情況下,不同類型的變量使用不同的模式來表示數據。這意味著,即使可以把一系列的位從一種類型的變量移動到另一種類型的變量中(也許它們占用的存儲空間相同,也許目標類型有足夠的存儲空間包含所有的源數據位),結果也可能與期望的不同。

因此,需要對數據進行類型轉換,而不是將數據位從一個變量一對一映射到另一個變量。類型轉換采用以下兩種形式:

隱式轉換:從類型A到類型B的轉換可在所有情況下進行,執行轉換的規則非常簡單,可以讓編譯器執行轉換。

顯式轉換:從類型A到類型B的轉換只能在某些情況下進行,轉換規則比較復雜,應進行某種類型的額外處理。

5.1.1 隱式轉換

隱式轉換不需要做任何工作,也不需要另外編寫代碼??紤]下面的代碼:

        var1 = var2;

如果var2的類型可以隱式地轉換為var1的類型,這條賦值語句就涉及隱式轉換。這兩個變量的類型也可能相同,此時就不需要隱式轉換。例如,ushort和char的值是可以互換的,因為它們都可以存儲0和65535之間的數字,在這兩種類型之間可以進行隱式轉換,如下面的代碼所示:

        ushort destinationVar;
        char sourceVar = 'a';
        destinationVar = sourceVar;
        WriteLine($"sourceVar val: {sourceVar}");
        WriteLine($"destinationVar val: {destinationVar}");

這里存儲在sourceVar中的值放在destinationVar中。在用兩個WriteLine()命令輸出變量時,得到如下結果:

        sourceVar val: a
        destinationVar val: 97

即使兩個變量存儲的信息相同,使用不同的類型解釋它們時,方式也是不同的。

簡單類型有許多隱式轉換;bool和string沒有隱式轉換,但數值類型有一些隱式轉換。表5-1列出了編譯器可以隱式執行的數值轉換(記住,char存儲的是數值,所以char被當作數值類型)。

表5-1 隱式數值轉換

不要擔心——不需要記住這個表格,因為很容易看出編譯器可以執行哪些隱式轉換。第3章中的表3-1、表3-2和表3-3列出了每種簡單數字類型的取值范圍。這些類型的隱式轉換規則是:任何類型A,只要其取值范圍完全包含在類型B的取值范圍內,就可以隱式轉換為類型B。

其原因是很簡單的。如果要把一個值放在變量中,而該值超出了變量的取值范圍,就會出問題。例如,short類型的變量可以存儲0-32767的數字,而byte可以存儲的最大值是255,所以如果要把short值轉換為byte值,就會出問題。如果short包含的值在256和32767之間,相應數值就不能放在byte中。

但是,如果short類型變量中的值小于255,就應能轉換這個值嗎?答案是可以。具體地說,雖然可以,但必須使用顯式轉換。執行顯式轉換有點類似于“我已經知道你對我這么做提出了警告,但我將對其后果負責”。

5.1.2 顯式轉換

顧名思義,在明確要求編譯器把數值從一種數據類型轉換為另一種數據類型時,就是在執行顯式轉換。因此,這需要另外編寫代碼,代碼的格式因轉換方法而異。在學習顯式轉換代碼前,首先分析如果不添加任何顯式轉換代碼,會發生什么情況。

例如,下面對上一節的代碼進行修改,試著把short值轉換為byte類型:

        byte destinationVar;
        short sourceVar = 7;
        destinationVar = sourceVar;
        WriteLine($"sourceVar val: {sourceVar}");
        WriteLine($"destinationVar val: {destinationVar}");

如果編譯這段代碼,就會產生如下錯誤:

        Cannot implicitly convert type 'short' to 'byte'. An explicit conversion exists
        (are you missing a cast? )

為成功編譯這段代碼,需要添加代碼,進行顯式轉換。最簡單的方式是把short變量強制轉換為byte類型(由上述錯誤字符串提出)。強制轉換就是強迫數據從一種類型轉換為另一種類型,其語法比較簡單:

        (<destinationType>)<sourceVar>

這將把<sourceVar>中的值轉換為<destinationType>類型。

注意:這只在某些情況下是可行的。彼此之間幾乎沒有什么關系的類型或根本沒有關系的類型不能進行強制轉換。

因此可以使用這個語法修改示例,把short變量強制轉換為byte類型:

        byte destinationVar;
        short sourceVar = 7;
        destinationVar = (byte)sourceVar;
        WriteLine($"sourceVar val: {sourceVar}");
        WriteLine($"destinationVar val: {destinationVar}");

得到如下結果:

        sourceVar val: 7
        destinationVar val: 7

在試圖把一個值轉換為不兼容的變量類型時,會發生什么呢?以整數為例,不能把一個大整數放到一個太小的數值類型中。按如下所示修改代碼就能證明這一點:

        byte destinationVar;
        short sourceVar = 281;
        destinationVar = (byte)sourceVar;
        WriteLine($"sourceVar val: {sourceVar}");
        WriteLine($"destinationVar val: {destinationVar}");

結果如下:

        sourceVar val: 281
        destinationVar val: 25

發生了什么?看看這兩個數字的二進制表示,以及可以存儲在byte中的最大值255:

        281 = 100011001
         25 = 000011001
        255 = 011111111

可以看出,源數據的最左邊一位丟失了。這會引發一個問題:如何確定數據是何時丟失的?顯然,當需要顯式地把一種數據類型轉換為另一種數據類型時,最好能夠了解是否有數據丟失了。如果不知道這些,就會發生嚴重問題。例如,財務應用程序或確定火箭飛往月球的軌道的應用程序。

一種方式是檢查源變量的值,將它與目標變量的取值范圍進行比較。還有另一個技術,就是迫使系統特別注意運行期間的轉換。在將一個值放在一個變量中時,如果該值過大,不能放在該類型的變量中,就會導致溢出,這就需要檢查。

對于為表達式設置所謂的溢出檢查上下文,需要用到兩個關鍵字—— checked和unchecked。按下述方式使用這兩個關鍵字:

        checked(<expression>)
        unchecked(<expression>)

下面對上一個示例進行溢出檢查:

        byte destinationVar;
        short sourceVar = 281;
        destinationVar = checked((byte)sourceVar);
        WriteLine($"sourceVar val: {sourceVar}");
        WriteLine($"destinationVar val: {destinationVar}");

執行這段代碼時,程序會崩潰,并顯示如圖5-1所示的錯誤信息(在OverflowCheck項目中編譯這段代碼)。

圖5-1

但在這段代碼中,如果用unchecked替代checked,就會得到與以前同樣的結果,不會出現錯誤。這與前面的默認做法是一樣的。

也可以配置應用程序,讓這種類型的表達式都和包含checked關鍵字一樣,除非表達式明確使用unchecked關鍵字(換言之,可以改變溢出檢查的默認設置)。為此,應修改項目的屬性:右擊Solution Explorer窗口中的項目,選擇Properties選項。單擊窗口左邊的Build,打開Build設置。

要修改的屬性是一個Advanced設置,所以單擊Advanced按鈕。在打開的對話框中,選中Check for arithmetic overflow/underflow選項,如圖5-2所示。默認情況下禁用這個設置,激活它可以提供上述checked行為。

圖5-2

5.1.3 使用Convert命令進行顯式轉換

前面章節中的許多“試一試”示例中使用的顯式類型轉換,與本章前面的示例有一些區別。前面使用ToDouble()等命令把字符串值轉換為數值,顯然,這種方式并不適用于所有字符串。

例如,如果使用ToDouble()把Number字符串轉換為double值,在執行代碼時,將看到如圖5-3所示的對話框。

圖5-3

可以看出,執行失敗。為成功執行此類轉換,所提供的字符串必須是數值的有效表達方式,該數還必須是不會溢出的數。數值的有效表達方式是:首先是一個可選符號(加號或減號),然后是0位或多位數字,一個可選的句點后跟一位或多位數字,接著是一個可選的e或E,后跟一個可選符號和一位或多位數字,除了還可能有空格(在這個序列之前或之后),不能有其他字符。利用這些可選的額外數據,可將-1.2451e-24這樣復雜的字符串識別為數值。

對于這些轉換要注意的一個問題是,它們總是要進行溢出檢查,checked和unchecked關鍵字以及項目屬性設置不起作用。

下面的示例包括本節介紹的許多轉換類型。它聲明和初始化許多不同類型的變量,再在它們之間進行隱式和顯式轉換。

試一試:類型轉換實踐:Ch05Ex01\Program.cs

(1)在C:\BegVCSharp\Chapter05目錄中創建一個新的控制臺應用程序Ch05Ex01。

(2)把下述代碼添加到Program.cs中:

        static void Main(string[] args)
        {
          short   shortResult, shortVal = 4;
          int     integerVal = 67;
          long    longResult;
          float   floatVal = 10.5F;
          double  doubleResult, doubleVal = 99.999;
          string  stringResult, stringVal = "17";
          bool    boolVal = true;
          WriteLine("Variable Conversion Examples\n");
          doubleResult = floatVal * shortVal;
          WriteLine($"Implicit, -> double: {floatVal} * {shortVal} -> { doubleResult }");
          shortResult = (short)floatVal;
          WriteLine($"Explicit, -> short:  {floatVal} -> {shortResult}");
          stringResult = Convert.ToString(boolVal) +
                  Convert.ToString(doubleVal);
          WriteLine($"Explicit, -> string: \"{boolVal}\" + \"{doubleVal}\" -> " +
                  $"{stringResult}");
          longResult = integerVal + ToInt64(stringVal);
          WriteLine($"Mixed, -> long:  {integerVal} + {stringVal} -> {longResult}");
          ReadKey();
        }

(3)執行代碼,結果如圖5-4所示。

圖5-4

示例說明

這個示例包含前面介紹的所有轉換類型,既有像前面簡短代碼示例中的簡單賦值,也有在表達式中進行的轉換。必須考慮這兩種情況,因為每個非一元運算符的處理都可能要進行類型轉換,而不僅是賦值運算符。例如:

        shortVal * floatVal

其中把一個short值與一個float值相乘。在這樣的指令中,沒有指定顯式轉換,所以如有可能,就會進行隱式轉換。在這個示例中,唯一有意義的隱式轉換是把short值轉換為float值(因為把float值轉換為short值需要進行顯式轉換),所以這里將使用隱式轉換。

也可以覆蓋這種行為,如下所示:

        shortVal * (short)floatVal

注意:有趣的是,兩個short值相乘的結果并不會返回一個short值。因為這個操作的結果很可能大于32767(這是short類型可以存儲的最大值),所以這個操作的結果實際上是int值。

這個轉換過程初看起來比較復雜,但只要按照運算符的優先級把表達式分解為不同的部分,就可以弄明白這個過程。

主站蜘蛛池模板: 黄山市| 巫溪县| 广昌县| 沧州市| 双城市| 乃东县| 昭苏县| 东乡县| 安达市| 宁化县| 广元市| 如东县| 正阳县| 布拖县| 广河县| SHOW| 喜德县| 玛纳斯县| 谷城县| 迭部县| 怀宁县| 洪湖市| 兴业县| 喜德县| 潮州市| 九江市| 长沙县| 常德市| 措勤县| 英德市| 衡山县| 曲周县| 常州市| 溧阳市| 正宁县| 阿图什市| 彭阳县| 佛冈县| 朝阳市| 罗江县| 古交市|