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

第3章C#中的引用類型和值類型

數(shù)據(jù)類型是.NET最基本的構(gòu)成元素,數(shù)據(jù)類型是該平臺(tái)下的C#開發(fā)語(yǔ)言的基本組成。每一個(gè)變量都要求定義為一個(gè)特定的類型,并且要求存儲(chǔ)在變量中的值只能是這種類型的值。變量既能保存值類型,也可以保存引用類型,還可以是指針。其中引用類型和值類型是.NET中最基本的數(shù)據(jù)類型。

3.1 引用類型和值類型簡(jiǎn)介

CLR(公共語(yǔ)言運(yùn)行時(shí))為.NET平臺(tái)提供了一個(gè)運(yùn)行環(huán)境,CLR支持兩種類型:引用類型和值類型。在.NET中的變量的類型通常是引用類型或者值類型。其中值類型包括:基元類型、枚舉和結(jié)構(gòu)。引用類型包括:類、字符串、標(biāo)準(zhǔn)模塊、接口、數(shù)組和委托。CLR中的數(shù)據(jù)類型示意圖如圖3-1所示。

圖3-1 CLR類型結(jié)構(gòu)

3.2 引用類型

C#預(yù)定義的引用類型包括object和string兩種類型。而用戶定義的引用類型可以是接口類型、類類型和委托類型。引用類型實(shí)例總是從托管堆上分配,引用類型實(shí)例內(nèi)存的回收通過垃圾收集器。

3.2.1 引用類型內(nèi)存分配

引用類型實(shí)例分配在托管堆上,變量保存了實(shí)例數(shù)據(jù)的內(nèi)存引用。引用類型可以是自描述類型、指針類型或接口類型。而自描述類型進(jìn)一步細(xì)分成數(shù)組和類類型。類類型則可以是用戶定義的類、裝箱的值類型和委托。通常聲明為以下類型:class、interface、delegate、object、string及其他的自定義引用類型時(shí),該變量即為引用類型。

在C#中有以下一些常用引用類型。

? 數(shù)組:(派生于System.Array);

? 指針:Indicator(指針);

? 接口:interface(接口)。

用戶使用定義的以下類型。

? 類:class(派生于System.Object);

? 委托:delegate(派生于System.Delegate)。

? object(System.Object的別名);

? 字符串:string(System.String的別名)。

【實(shí)例3-1】本例將創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,當(dāng)用戶輸入自己的名字后,顯示“歡迎**使用C# 4.0開發(fā)平臺(tái)!”。

(1)創(chuàng)建控制臺(tái)應(yīng)用程序啟動(dòng)Visual Studio 2010,選擇“文件”→“新建”命令,在“項(xiàng)目”選項(xiàng)卡中選擇“控制臺(tái)應(yīng)用程序”,創(chuàng)建一個(gè)名稱為“3.1”的控制臺(tái)應(yīng)用程序,如圖3-2所示。

圖3-2 創(chuàng)建3.1的項(xiàng)目

(2)編寫字符串引用類型的代碼,如下所示:

    01          static void Main(string[] args)
    02          {
    03             string yourname;                          //字符串引用類型變量
    04             Console.WriteLine("請(qǐng)輸入您的昵稱!");
    05             string name = Console.ReadLine();
    06             yourname = NewStr(name);
    07             Console.WriteLine(yourname);
    08             Console.ReadLine();
    09          }
    10
    11          public static string NewStr(string yourname) //公共靜態(tài)字符函數(shù)
    12          {
    13             string newstr;                             //字符串引用類型變量
    14             newstr = "歡迎" + yourname + "使用C# 4.0開發(fā)平臺(tái)!";
    15             return newstr;
    16          }

【代碼解析】第11~15行創(chuàng)建公共靜態(tài)字符函數(shù)NewStr()。

【運(yùn)行效果】代碼編寫完成之后,按“F5”鍵或者單擊工具欄中的“啟動(dòng)調(diào)試”按鈕,當(dāng)用戶輸入“FREEMEN”后,按“Enter”鍵,顯示結(jié)構(gòu)如圖3-3所示。

圖3-3 運(yùn)行結(jié)果

引用類型事實(shí)上保存一個(gè)指向它引用的對(duì)象的內(nèi)存地址。上面的代碼段中有兩個(gè)字符串變量(yourname和newstr)引用了同一個(gè)string對(duì)象。改變某一個(gè)引用指向的對(duì)象的屬性同時(shí)也會(huì)影響所有其他指向這個(gè)對(duì)象的引用。當(dāng)字符串newstr發(fā)生變化時(shí),字符串yourname也跟著發(fā)生變化。產(chǎn)生這種行為的原因是由于string對(duì)象是恒定的,也就是說(shuō),一旦一個(gè)string對(duì)象被創(chuàng)建,它的值就不能再修改,所以當(dāng)改變一個(gè)字符串變量的值的時(shí)候,僅僅是新創(chuàng)建了一個(gè)包含修改內(nèi)容的新的string對(duì)象。

3.2.2 引用類型賦值

引用類型賦值時(shí),將會(huì)產(chǎn)生一個(gè)對(duì)該堆上同一個(gè)對(duì)象的新引用。下面將以引用類型中的class類型為例,實(shí)現(xiàn)引用類型的賦值及原始值的覆蓋。

【實(shí)例3-2】本節(jié)將創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,實(shí)現(xiàn)引用類型的賦值及原始值的覆蓋。

(1)創(chuàng)建控制臺(tái)應(yīng)用程序啟動(dòng)Visual Studio 2010,選擇“文件”→“新建”菜單命令,在“項(xiàng)目”選項(xiàng)卡中選擇“控制臺(tái)應(yīng)用程序”,創(chuàng)建一個(gè)名稱為“3.2”的控制臺(tái)應(yīng)用程序。

(2)編寫Person類代碼,并在Program類中實(shí)現(xiàn)引用類型的賦值,代碼如下:

    01      class Person
    02      {
    03          public string fullName;                                     //聲明引用類型的字符變量
    04          public int age;                                              //聲明int變量
    05          public Person(string n, int a)                              //定義Person
    06          {
    07             fullName = n;
    08             age = a;
    09          }
    10          public void PrintInfo()                                     //輸出
    11          {
    12             Console.WriteLine("{0} 的年齡是 {1} 歲", fullName, age);
    13          }
    14      }
    15      class Program
    16      {
    17          public static void ArrayOfObjects(params object[] list)    //數(shù)組對(duì)象操作方法
    18          {
    19             for (int i = 0; i < list.Length; i++)                   //使用循環(huán)操作
    20             {
    21                 if (list[i] is Person)
    22                 {
    23                    ((Person)list[i]).PrintInfo();
    24                 }
    25                 else
    26                    Console.WriteLine(list[i]);
    27             }
    28             Console.WriteLine();
    29          }
    30          public static void SendAPersonByValue(Person p)
    31          {
    32             //為age賦值
    33             p.age = 99;
    34             p = new Person("張三", 999);                             //實(shí)例化Person
    35          }
    36          public static void SendAPersonByReference(ref Person p)
    37          {
    38             //修改age值
    39             p.age = 555;
    40             p = new Person("張三", 999) ;                            //實(shí)例化Person
    41          }
    42          public static void Main()
    43          {
    44             Console.WriteLine("***** 為Person類設(shè)置一個(gè)新的引用類型值*****");
    45             Person fred = new Person("李四", 12);
    46             Console.WriteLine("值被覆蓋前的person對(duì)象是:");
    47             fred.PrintInfo();
    48             SendAPersonByValue(fred);
    49             Console.WriteLine("值被覆蓋前的person對(duì)象是:");
    50             fred.PrintInfo(); //年齡的更改會(huì)有效果,但是重新賦值不會(huì)起效
    51             Console.WriteLine("\n***** 通過person對(duì)象參考 *****");
    52             Person mel = new Person("王五", 23);
    53             Console.WriteLine("值被覆蓋前的person對(duì)象是:");
    54             mel.PrintInfo();
    55             SendAPersonByReference(ref mel);
    56             Console.WriteLine("值被覆蓋前的person對(duì)象是:");
    57             mel.PrintInfo();                                         //被重新賦予另外一個(gè)對(duì)象
    58             Console.ReadLine();
    59          }
    60     }

【代碼解析】第1~14行創(chuàng)建Person類,在該類中完成年齡的定義、輸出。第17~29行使用數(shù)組對(duì)象操作方法操作Person類。第45行實(shí)例化Person類。

上面代碼完全是對(duì)同一Person對(duì)象的操作,被調(diào)用者可以改變對(duì)象的狀態(tài)數(shù)據(jù)的值和所引用的對(duì)象,可重新賦值。

【運(yùn)行效果】代碼編寫完成之后,按“F5”鍵或者單擊工具欄中的“啟動(dòng)調(diào)試”按鈕,顯示結(jié)果如圖3-4所示。

圖3-4 運(yùn)行結(jié)果

3.3 值類型

C#中值類型實(shí)例通常分配在線程的堆棧上,并且不包含任何指向?qū)嵗龜?shù)據(jù)的指針,因?yàn)樽兞勘旧砭桶似鋵?shí)例數(shù)據(jù)。在托管代碼中,類型決定了類型實(shí)例的分配位置,而使用類型的開發(fā)人員對(duì)此沒有控制權(quán)。值類型實(shí)例不受垃圾收集器的控制。

3.3.1 值類型內(nèi)存分配

值類型主要包括簡(jiǎn)單類型、結(jié)構(gòu)體類型和枚舉類型等。通常聲明為以下類型:int、char、float、long、bool、double、struct、enum、short、byte、decimal、sbyte、uint、ulong、ushort等時(shí),該變量即為值類型。C#中的主要值類型如表3-1所示。

表3-1 值類型表

需要說(shuō)明,C#的所有值類型均隱式派生自System.ValueType,每種值類型均有一個(gè)隱式的默認(rèn)構(gòu)造函數(shù)來(lái)初始化該類型的默認(rèn)值。例如:

    int i = new int();
    //等價(jià)于:
    Int32 i = new Int32();
    //等價(jià)于:
    int i = 0;
    //等價(jià)于:
    Int32 i = 0;

【實(shí)例3-3】本例將創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,實(shí)現(xiàn)值類型的操作。

(1)創(chuàng)建控制臺(tái)應(yīng)用程序。

啟動(dòng)Visual Studio 2010,選擇“文件”→“新建”菜單命令,在“項(xiàng)目”選項(xiàng)卡中選擇“控制臺(tái)應(yīng)用程序”,創(chuàng)建一個(gè)名稱為“3.3”的控制臺(tái)應(yīng)用程序。

(2)編寫值類型操作其本身的代碼,代碼如下:

    01          static void Main(string[] args)
    02          {
    03             Console.WriteLine("所設(shè)置的Int類型的值是:");
    04             Console.WriteLine(ReturnValue());
    05             Console.ReadLine();
    06          }
    07          public class MyInt
    08          {
    09             public int MyValue;              //聲明值類型變量MyValue
    10          }
    11
    12          public static int ReturnValue()     //靜態(tài)函數(shù)返回int值
    13          {
    14             MyInt x = new MyInt();
    15             x.MyValue = 3;
    16             MyInt y = new MyInt();
    17             y = x;
    18             y.MyValue = 4;
    19             return x.MyValue;                //返回int值
    20       }

【代碼解析】第9行聲明值類型變量MyValue。第12~20行靜態(tài)函數(shù)ReturnValue()返回int值。第14行實(shí)例化MyInt類,并為該類的MyValue賦值。

【運(yùn)行效果】代碼編寫完成之后,按“F5”鍵或者單擊工具欄中的“啟動(dòng)調(diào)試”按鈕,顯示結(jié)果如圖3-5所示。

圖3-5 運(yùn)行結(jié)果

3.3.2 值類型賦值

值類型包括數(shù)值類型、枚舉和結(jié)構(gòu),它們都分配在棧上,一旦離開定義的作用域,立即就會(huì)被從內(nèi)存中刪除。當(dāng)一個(gè)值類型賦值給另一個(gè)值類型的時(shí)候,默認(rèn)情況下完成的是一個(gè)成員到另一個(gè)成員的復(fù)制。就數(shù)值和布爾型而言,唯一要復(fù)制的就是變量本身的值。

值類型賦值的時(shí)候,是復(fù)制各個(gè)值到賦值目標(biāo),實(shí)際上各自在棧中都有存在,對(duì)一個(gè)值的操作不會(huì)影響另一個(gè)。

【實(shí)例3-4】本例將創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,來(lái)說(shuō)明值類型賦值的時(shí)候,是復(fù)制各個(gè)值到賦值目標(biāo),實(shí)際上各自在棧中都有存在,對(duì)一個(gè)值的操作不會(huì)影響另一個(gè)。

(1)創(chuàng)建控制臺(tái)應(yīng)用程序。

啟動(dòng)Visual Studio 2010,選擇“文件”→“新建”菜單命令,在“項(xiàng)目”選項(xiàng)卡中選擇“控制臺(tái)應(yīng)用程序”,創(chuàng)建一個(gè)名稱為“3.4”的控制臺(tái)應(yīng)用程序。

(2)代碼如下所示:

    01      //定義一個(gè)結(jié)構(gòu)
    02      struct MyPoint
    03      {
    04          public int x, y;
    05      }
    06      // 類將作為結(jié)構(gòu)使用
    07      class ShapeInfo
    08      {
    09          public string infoString;
    10          public ShapeInfo(string info)
    11          { infoString = info; }
    12      }
    13
    14      struct MyRectangle
    15      {
    16          public ShapeInfo rectInfo;
    17          public int top, left, bottom, right;
    18          public MyRectangle(string info)
    19          {
    20             rectInfo = new ShapeInfo(info);
    21             top = left = 10;
    22             bottom = right = 100;
    23          }
    24      }
    25      class ValRefClass
    26      {
    27          static void Main(string[] args)
    28          {
    29             Console.WriteLine("***** 值類型/ 引用類型 *****");
    30             //在棧中
    31             MyPoint p = new MyPoint();
    32             Console.WriteLine("-> Creating p1");
    33             MyPoint p1 = new MyPoint();
    34             p1.x = 100;
    35             p1.y = 100;
    36             Console.WriteLine("-> Assigning p2 to p1");
    37             MyPoint p2 = p1;
    38             // P1
    39             Console.WriteLine("p1.x = {0}", p1.x);//100
    40             Console.WriteLine("p1.y = {0}", p1.y);//100
    41             // P2.
    42             Console.WriteLine("p2.x = {0}", p2.x);//100
    43             Console.WriteLine("p2.y = {0}", p2.y);//100
    44             // 修改p2.x.不改變p1.x.
    45             Console.WriteLine("-> Changing p2.x to 900");
    46             p2.x = 900;
    47             //顯示
    48             Console.WriteLine("-> Here are the X values again...");
    49             ;//100,如果將MyPoint改成類類型,此處會(huì)顯示900。
    50             Console.WriteLine("p1.x = {0}", p1.x)
    51             Console.WriteLine("p2.x = {0}", p2.x);//900
    52             Console.WriteLine();
    53             // 創(chuàng)建第一個(gè)MyRectangle.
    54             Console.WriteLine("-> Creating r1");
    55             MyRectangle r1 = new MyRectangle("This is my first rect");
    56             // 將一個(gè)新的MyRectangle賦值給r1
    57             Console.WriteLine("-> Assigning r2 to r1");
    58             MyRectangle r2;
    59             r2 = r1;
    60             //修改r2的值
    61             Console.WriteLine("-> Changing all values of r2");
    62             r2.rectInfo.infoString = "This is new info!";
    63             r2.bottom = 4444;
    64             //顯示s
    65             Console.WriteLine("-> Values after change:");
    66             Console.WriteLine("-> r1.rectInfo.infoString:{0}", r1.rectInfo.infoString);
    67             Console.WriteLine("-> r2.rectInfo.infoString:{0}", r2.rectInfo.infoString);
    68             Console.WriteLine("-> r1.bottom: {0}", r1.bottom);//100
    69             Console.WriteLine("-> r2.bottom: {0}", r2.bottom);//4444
    70             Console.ReadLine();
    71          }
    72       }

【代碼解析】第4行為結(jié)構(gòu)MyPoint定義兩個(gè)變量,供后面調(diào)用。

【運(yùn)行效果】代碼編寫完成之后,按“F5”鍵或者單擊工具欄中的“啟動(dòng)調(diào)試”按鈕,顯示結(jié)果如圖3-6所示。

圖3-6 運(yùn)行效果

3.4 引用類型和值類型的區(qū)別

引用類型和值類型的區(qū)別如下。

? 值類型對(duì)象有兩種表示:未裝箱形式和裝箱形式,而引用類型總是裝箱形式。

? 當(dāng)定義自己的值類型時(shí),應(yīng)該重寫Equals方法和GetHashCode方法。

? 值類型中不可以有任何的抽象方法,不可以引入任何新的虛方法,所有的方法都隱含為sealed方法。

? 當(dāng)一個(gè)引用類型變量被創(chuàng)建時(shí),它被初始化為null。值類型變量總是包含一個(gè)符合它的類型的值。

? 當(dāng)將一個(gè)值類型變量賦值給另一個(gè)值類型變量,會(huì)進(jìn)行一個(gè)字段對(duì)字段的復(fù)制,而將一個(gè)引用類型變量賦值給另一個(gè)引用類型變量時(shí),只會(huì)復(fù)制內(nèi)存地址。

? 兩個(gè)或多個(gè)引用類型變量可以指向托管堆中的同一個(gè)對(duì)象,而每個(gè)值類型變量都有一份自己的對(duì)象數(shù)據(jù)副本。

? 值類型實(shí)例在內(nèi)存回收時(shí)不可能收到任何通知。

System.Runtime.InteropServices.StructLayout特性用于指示CLR是按指定的順序來(lái)存儲(chǔ)類型實(shí)例的字段,還是以任何CLR認(rèn)為合適的順序排列字段。C#編譯器為引用類型選擇的是LayoutKind.Auto方式,而為值類型選擇的是LayoutKind.Sequential方式。

值類型和引用類型的區(qū)別如表3-2所示。

表3-2 值類型和引用類型區(qū)別表

3.5 C# 4.0中的新特性:查看調(diào)用層(View Call Hierarchy)

該特性主要用于查看函數(shù)和屬性,在編程過程中遇到不明用途的函數(shù),可在該函數(shù)上面單擊鼠標(biāo)右鍵,該特性會(huì)告訴用戶的函數(shù)使用分層列表,如圖3-7所示。

圖3-7 調(diào)用層次

為了查看某個(gè)函數(shù)的詳細(xì)信息可以在圖3-7中單擊查看調(diào)用層次,它會(huì)顯示一個(gè)窗體,詳細(xì)顯示對(duì)應(yīng)函數(shù)的信息,如圖3-8所示。

圖3-8 函數(shù)信息圖

在層次結(jié)構(gòu)中選擇窗口函數(shù),調(diào)用它會(huì)顯示參數(shù)和函數(shù)調(diào)用的位置的詳細(xì)信息,如圖3-9所示。

圖3-9 函數(shù)詳細(xì)信息

3.6 小結(jié)

本章講解了C#中的數(shù)據(jù)類型,C#中每一種類型要么是值類型,要么是引用類型。所以每個(gè)對(duì)象要么是值類型的實(shí)例,要么是引用類型的實(shí)例。值類型的實(shí)例通常是在線程棧上分配的(靜態(tài)分配)。引用類型的對(duì)象總是在進(jìn)程堆中分配(動(dòng)態(tài)分配)的。

3.7 練習(xí)

一、填空題

1.CLR的全稱是(  )。

2.常見的值類型包括(  )。

3.常見的引用類型包括(  )。

4.值類型存放的位置(  )。

5.引用類型存放的位置(  )。

二、簡(jiǎn)答題

1.簡(jiǎn)述C#中常見的數(shù)據(jù)類型。

2.說(shuō)說(shuō)值類型和引用類型的區(qū)別。

三、上機(jī)題

嘗試使用C#創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,輸入一個(gè)自然數(shù),顯示結(jié)果在輸入的自然數(shù)上加10,運(yùn)行效果要求與如圖2-3所示一致。

主站蜘蛛池模板: 邹城市| 大田县| 望都县| 潜山县| 崇阳县| 宝清县| 平罗县| 玛多县| 凤冈县| 尉氏县| 翁牛特旗| 新巴尔虎右旗| 周宁县| 壶关县| 呼伦贝尔市| 抚州市| 肃宁县| 清流县| 江北区| 大埔县| 同心县| 武邑县| 武城县| 青铜峡市| 扶余县| 分宜县| 九龙县| 和龙市| 长岛县| 苗栗县| 新巴尔虎左旗| 射洪县| 商丘市| 丹棱县| 青河县| 临城县| 苏州市| 枝江市| 伊吾县| 龙胜| 嵩明县|