天天看點

C#-12 轉換

C#基礎之轉換

一 什麼是轉換

轉換是接受一個類型的值并使用它作為另一個類型的等價值的過程。

下列代碼示範了将1個short類型的值強制轉換成byte類型的值。

short var1 = 5;
byte var2 = 10;

var2 = (byte) var1;    //強制轉換,将var1的值轉換成byte類型      
C#-12 轉換

二 隐式轉換

有些類型的轉換不會丢失資料與精度,如将8位的值轉換成16位的值是非常容易的,不會丢失精度。

C#語言會自動進行轉換,稱為隐式轉換。

從位數更少的源轉換成位數更多的目标類型時,目标中多出的位需要用0或1填充。

當從更小的無符号類型轉換為更大的無符号類型時,目标類型多出的最高位都以0進行填充,這叫做零擴充。

下圖示範使用零擴充将8位的10轉換成16位的10:

C#-12 轉換

對于有符号類型的轉換而言,額外的高位用源表達式的符号位進行填充,這樣就維持了被轉換值的正确符号和大小。 

下圖示範符号擴充:

C#-12 轉換

三 顯示轉換和強制轉換

嘗試将ushort值1365轉換為byte值,會導緻資料丢失。

C#-12 轉換

對于預定義類型,C#會自動将一個資料類型轉換成另一個資料類型,但隻是針對那些從源類型到目标類型不會發生資料丢失的情況。

而對于從源類型到目标類型轉換會發生資料丢失的情況,C#不提供自動轉換,就需要使用顯示轉換。這叫做強制轉換表達式。

四 轉換的類型

有很多标準的、預定義的用于數字和引用的轉換。

C#-12 轉換

除了标準轉換,還可以為自定義類型定義隐式轉換和顯示轉換。

裝箱:将值類型轉換為object類型、System.ValueType類型。

拆箱:将一個裝箱的值轉換為原始類型。

五 引用轉換

引用類型對象有記憶體中的兩部分組成:引用和資料。

由引用儲存的那部分資訊是它指向的資料類型。

引用轉換接受源引用并傳回一個指向堆中同一位置的引用,但是把引用标記為轉換的目标類型。

下例給出兩個引用變量,myVar1和myVar2,它們指向記憶體中的相同對象。

class Program
{
    static void Main(string[] args)
    {
        B myVar1 = new B();
        A myVar2 = (A)myVar1;   //将myVar1作為A類的引用傳回

        Console.WriteLine(myVar1.Field1);   //正确
        Console.WriteLine(myVar2.Field2);   //錯誤,Field2對于myVar2不可見
    }
}

class A { public int Field1; }

class B : A { public int Field2; }      

5.1 隐式引用轉換

C#可以自動實作隐式引用轉換。

  • 所有引用類型都可以被隐式轉換為object類型;
  • 任何類型可以隐式轉換到它繼承的接口;
  • 類可以隐式轉換到它繼承鍊中的任何類和它實作的任何接口。

5.2 顯示引用轉換

顯示引用轉換是從一個普通類型到一個更精确類型的引用轉換。

顯示轉換包括:

  • 從object到任何引用類型的轉換;
  • 從基類到從它繼承的類的轉換。

六 裝箱轉換和拆箱轉換

6.1 裝箱轉換

裝箱是一種隐式轉換,它接受值類型的值,根據這個值在堆中建立一個完整的引用類型對象并傳回對象引用。

6.2 拆箱轉換

拆箱是把裝箱後的對象轉換回值類型對象的過程。

拆箱是顯示轉換。

系統在把值拆箱成ValueTypeT時執行了如下步驟:

  • 它檢測到要拆箱的對象實際是ValueTypeT的裝箱值;
  • 它把對象的值複制到變量。

七 使用者自定義轉換

除了标準轉換,我們還可以為類和結構自定義隐式轉換和顯示轉換。

 文法如下:

public static implicit operator TargetType (SourceType Identifier)
{
       . . .
       return ObjectOfTargetType   
}            

使用者自定義轉換的限制:

  • 隻可以為類和結構定義使用者自定義轉換;
  • 不能重定義标準隐式轉換或顯示轉換;
  • 對于源類型S和目标類型T,S和T必須是不同類型;
  • S和T不能通過繼承關聯,S和T都不能是接口類型或object類型;
  • 轉換運算符必須是S或T的成員。 

implicit:隐式轉換

explicit:顯示轉換,需要強制轉換表達式

下面示範一個使用者自定義轉換的例子:

class Program
{
    static void Main(string[] args)
    {
        Person bill = new Person("bill", 25);
        int age = bill;     //将Person對象轉換為int
        Console.WriteLine($"Person Info :{bill.Name},{age}");

        Person none = 35;   //将int轉換為Person對象
        Console.WriteLine($"Person Info :{none.Name},{none.Age}");

        //輸出:
        //Person Info :bill,25
        //Person Info :none,35
    }
}

class Person
{
    public string Name;
    public int Age;

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    //将Person隐式轉換為int
    public static implicit operator int(Person p)
    {
        return p.Age;
    }

    //将int隐式轉換為Person
    public static implicit operator Person(int i)
    {
        return new Person("none", i);
    }
}      

八 is運算符和as運算符

有些轉換是非法的,會在運作時抛出一個InvalidCastException異常。我們可以使用Is運算符來檢查轉換是否會成功完成。

as運算符和強制轉換運算符類似,隻是它不抛出異常。如果轉換失敗,它傳回null而不是抛出異常。

class Program
{
    static void Main(string[] args)
    {
        var student = new Student();
        student.Name = "jack";
        student.Age = 16;
        // is 第1種用法:檢測student是否能轉換為Person類型,如果可以,則傳回true
        if (student is Person)
        {
            Console.WriteLine("stundent is Person");
        }
        // is 第2種用法:檢測student是否能轉換為Person類型,如果可以,則傳回true,并将其轉換指派給Person類型的變量p
        if (student is Person p)
        {
            Console.WriteLine($"Person p:{p.Name},{p.Age}");
        }

        // as 用法
        var p1 = student as Person;
        if (p1 != null)
        {
            Console.WriteLine($"Person p:{p1.Name},{p1.Age}");
        }
    }
}

class Person
{
    public string Name;
    public int Age;
}

class Student : Person { }      

 

c#