微軟.NET FRAMEWORK 2.0 程序設計

**************************

***第一章FRAMEWORK基礎*****

**************************
.NET Framework 是 Microsoft Windows 組件旨在支持下一代應用程序和服務的整數。 .NET 很多基礎知

識框架將開發人員曾在其他面向對象的開發環境中所熟悉 ; 但是,.NET Framework 還包括到甚至最有

經驗的開發人員的新的很多新的元素。本章概述了包括這本書中的每個其他章所需的知識的.NET

Framework 編程。
--------------------------------------------------------------------------------
注意:
如果您曾與版本的.NET Framework 2.0 版之前公佈,大部分這將熟悉。 但是,.NET Framework 2.0 版

包括若干新功能: 泛型、 分部類和類型轉發 (所有 “ 構造類 ” 第 3 課中所述)。
--------------------------------------------------------------------------------
本章中的考試目標:
一:通過使用.NET Framework 2.0 系統類型管理.NET Framework 應用程序中的數據。 (請參閱 System

命名空間)
1、值類型
2、引用類型
3、屬性
4、泛型類型
5、異常類
6、裝箱和拆箱
7、TypeForwardedToAttribute 類
二:實現.NET Framework 接口要遵守標準的組件。 (請參閱 System 命名空間)
1、IComparable 接口
2、IDisposable 接口
3、IConvertible接口
4、ICloneable  接口
5、IEquatable  接口
6、IFormattable接口
三:控制通過使用事件和委託的.NET Framework 應用程序組件之間的交互。 (請參閱 System 命名空間


1、委託類
2、事件類
3、EventHandler 委託
在開始之前
    本書假定您使用.NET Framework 1.0、 NET Framework 1.1 和 NET Framework 2.0 有最少兩至三年

的經驗開發基於 Web 的 Microsoft 基於 Windows 或分佈式應用程序。 候選人應把有工作知識的

Microsoft Visual Studio 2005。 在開始之前您應該熟悉 Microsoft Visual Basic 或 C#,屬熟悉下列

任務:
1、在 Visual Studio 使用 Visual Basic 或 C# 中創建控制檯或 WindowsForms 應用程序。
2、將命名空間和系統類庫的引用添加到項目。
3、在 Visual Studio 中運行項目設置斷點、 逐句通過代碼,觀看變量的值。

====================
====第 1 課: 使用值類型===

====================
    在.NET Framework 主要數字和布爾的類型中,最簡單類型是值類型。 值類型是包含其數據直接而不

是包含對其他地方存儲在內存中數據的引用的變量。 值類型的實例存儲在內存調用堆棧的運行庫可以創

建、 讀取、 更新,和與最低開銷快速刪除它們的區域。
--------------------------------------------------------------------------------
關於參考類型更多信息
有關引用類型相關的信息請參閱第 2 課。
--------------------------------------------------------------------------------
有三種一般值類型:
1、布爾類型
2、用戶自定義類型
3、枚舉類型
每一種類型都是從 System.Value 基類型派生的。 以下各節介紹如何使用這些不同類型。
本課程後您將能夠:
1、選擇最有效的內置值類型
2、聲明值類型
3、創建你自己的類型
4、使用枚舉類型
估計課時間: 30 分鐘
內置值類型
內置類型是生成其他類型的基類型。所有內置數值類型都是值類型。您選擇基於您希望使用的值的大小和

精度您需要的級別爲數值類型。
    這些數值類型被如此頻繁使用 Visual Basic 和 C# 爲它們定義別名。 使用別名等同於使用完整的

類型名稱,因此大多數程序員使用較短的別名。 除了,數值類型列出在表 1-2 中,非數值數據類型也是

值類型。在框架有近 300 多個值類型,基本滿足了需要。值類型變量之間分配時,數據和變量在不同的

堆棧上。此行爲是不同的和第 2 課中討論的引用類型。
即使值類型通常表示簡單值,他們仍作爲對象。 換句話說,您可以對它們調用方法。其實,它是通用的

顯示值作爲文本時使用 ToString 方法。 從基本 System.Object 類型重寫 ToString
注意 對象基類
    在.NET Framework 中所有類型都派生自 System.Object。 這種關係有助於建立使用整個 Framework

通用類型系統。
如何聲明值類型
如果要使用一個類型,你必須生命一個該類型的實例。值類型有一個隱式的構造函數,因此它們自動聲明

實例化類型;不必像類那樣包含new關鍵字。構造函數分配默認值通常是NULL或0到新實例。但下面的代碼

所示應始終顯式在聲明變量塊初始化內:
bool b = false;
如果要能夠確定是否尚未分配一個值,聲明爲空的變量。例如如果您存儲數據是從窗體和用戶沒有問題並

沒有回答問題,您應存儲空值。 以下代碼允許爲是 true、 false,或其他的布爾變量:
// C#
Nullable<bool> b = null;
//簡寫表示法
bool? b = null;
作爲空啓用 HasValue 和 Value 成員聲明變量。 有值用於檢測已設置一個值:
// C#
if (b.HasValue)Console.WriteLine("b is {0}.", b.Value);
else Console.WriteLine("b is not set.");
怎樣創建用戶自定義類型
   用戶定義的類型也稱爲結構或只是結構,用於創建它們在語言關鍵字後。與其它值用戶定義類型的實

例存儲在堆棧上,並且他們直接包含其數據。 在大多數其它方面幾乎與類的行爲相同。
   結構是一個複合使其易於使用的相關數據的其他類型。最簡單的例子是 System.Drawing.Point 其中

包含定義水平和垂直座標的一點的 X 和 Y 整數屬性。 Point 結構簡化使用座標通過提供構造函數和此

處顯示的成員:
// C# - Requires reference to System.Drawing
// Create point
System.Drawing.Point p = new System.Drawing.Point(20, 30);
// Move point diagonally
p.Offset(-1, -1);
Console.WriteLine("Point X {0}, Y {1}", p.X, p.Y);
    通過使用 Visual Basic 中的結構關鍵字或 struct 關鍵字在 C# 中的定義您自己的結構。 例如以

下代碼創建一個類型,由構造函數設置的最小和最大值之間循環通過一套整數:
// C#
struct Cycle
{
  // Private fields
  int _val, _min, _max;
  // Constructor
  public Cycle(int min, int max)
  {
    _val = min;
    _min = min;
    _max = max;
  }
  public int Value
  {
    get { return _val; }
    set
       {
        if (value > _max)
        _val = _min;
        else
       {
    if (value < _min)
    _val = _max;
    else
    _val = value;
   }
  }
}
   public override string ToString()
   {
     return Value.ToString();
   }
   public int ToInteger()
   {
    return Value;
   }
   // Operators (new in .NET 2.0)
   public static Cycle operator +(Cycle arg1, int arg2)
   {
     arg1.Value += arg2;
     return arg1;
   }
   public static Cycle operator -(Cycle arg1, int arg2)
   {
    arg1.Value -= arg2;
    return arg1;
   }
}
您可以使用此結構來表示項目,如程度的旋轉或宿舍一足球場遊戲一個固定範圍內重複,如下所示:
// C#
Cycle degrees = new Cycle(0, 359);
Cycle quarters = new Cycle(1, 4);
for (int i = 0; i <= 8; i++)
{
degrees += 90; quarters += 1;
Console.WriteLine("degrees = {0}, quarters = {1}", degrees, quarters);
}
週期示例可以輕鬆地轉換至爲引用類型的值類型通過類更改結構/結構關鍵字。 如果進行的更改則應週期

類的實例會撥在託管堆上而不是 12 字節上堆棧 (爲每個專用整數字段的 4 個字節) 和兩個變量結果

在兩個變量指向同一個實例之間的分配。
類似功能時結構是通常比類更有效。
    您應定義一個結構而不是如果該類型將執行的類作爲值更好地鍵入比引用類型。 具體來說,結構類

型應滿足所有這些準則:
1、邏輯上表示單個值
2、有一個實例大小小於 16 字節
3、將創建後不會更改
4、不會被轉換爲引用類型
怎樣創建枚舉類型
    枚舉是具有固定值的相關的符號。 使用枚舉來使用您的類的開發人員提供的選擇列表。 例如以下枚

舉包含一組的標題:
// C#
enum Titles : int { Mr, Ms, Mrs, Dr };
如果您創建了標題類型實例的 Visual Studio 將顯示可用值的列表時將一個值分配給變量,。 但變量的

值是一個整數,是易於輸出而它的值如下所示的符號名稱在這裏:
// C#
Titles t = Titles.Dr;
Console.WriteLine("{0}.", t); // 顯示 "Dr."
    枚舉旨在簡化編碼和改善代碼可讀性,使您可以使用有意義的符號而不是簡單的數值。 時使用您的

類型的開發人員必須從一組有限的一個值,選擇的選擇,請使用枚舉。
聲明和使用值類型
以下練習演示如何創建和使用結構以及如何創建枚舉。 如果遇到完成一次,已完成的問題項目有代碼文

件夾中附帶 CD 上:
行使 1: 創建一個結構
在本練習您將創建一個簡單的結構與多個公共成員。
1.使用 Visual Studio 創建新的控制檯應用程序項目。 該項目命名爲CreateStruct。
2.創建新的結構名爲 Person,按下面的代碼演示。
// C#
struct Person
{
}
3.在Person結構裏生命三個公共成員
❑ firstName (a String)
❑ lastName (a String)
❑ age (an Integer)
以下代碼演示了這一點:
// C#
public string firstName;
public string lastName;
public int age;
創建一個構造函數定義所有三個成員變量,如下面的代碼演示
// C#
public Person(string _firstName, string _lastName, int _age)
{
  firstName = _firstName;
  lastName = _lastName;
  age = _age;
}
.重寫 ToString 方法以顯示Person firstName、 lastName和age。 以下代碼演示了這一點:
public override string ToString()
{
   return firstName + " " + lastName + ", age " + age;
}
6.在控制檯應用程序主方法編寫代碼以創建結構的一個實例,並將該實例傳遞給該 Console.WriteLine

方法 下面的代碼演示了:
// C#
Person p = new Person("Tony", "Allen", 32);
Console.WriteLine(p);
7.運行控制檯應用程序以驗證正確工作。
行使 2: 將枚舉添加到一個結構
在本練習您將擴展運動 1 中創建加入枚舉結構。
1.打開計劃 1 中創建的項目。
2.聲明人結構中的新枚舉。 名稱,枚舉Genders並指定兩個可能值: 男性和女。 下面的代碼示例
演示了這種情況:
// C#
public enum Genders : int { Male, Female };
3.添加公共成員的鍵入Genders,和修改Person構造函數接受的性別實例。 以下代碼演示了這一點:
// C#
public string firstName;
public string lastName;
public int age;
public Genders gender;
public Person(string _firstName, string _lastName, int _age, Genders _gender)
{
  firstName = _firstName;
  lastName = _lastName;
  age = _age;
  gender = _gender;
}
修改Person.ToString 方法
// C#
public override string ToString()
{
  return firstName + " " + lastName + " (" + gender + "), age " + age;
}
5.按下面的代碼示例演示修改主代碼以適當構造的的 Person 類實例:
// C#
static void Main(string[] args)
{
  Person p = new Person("Tony", "Allen", 32, Person.Genders.Male);
  Console.WriteLine(p.ToString());
}
6.運行控制檯應用程序以驗證正確工作。
本課摘要
1、.NET Framework 包括大量的內置類型可以直接使用或使用構建自己的自定義類型。
2、值類型直接包含其數據提供優良的性能。 值類型卻限於存儲很小塊的數據的類型。 在 N 所有值類型

都的 16 個字節或較短。
3、可創建用戶定義類型存儲多個值和方法。 在面向對象的開發環境中的應用程序邏輯一大部分將存儲在

用戶定義類型。
4、枚舉提供符號爲一組值,從而改善代碼可讀性。
課後習題
您可以使用以下問題測試您的有關知識中第 1 課 “ 使用值類型 ”。 如果您想檢討他們以電子形式,

附帶 CD 上也有問題。

===============================
========第 2 課: 使用公共引用類型=====
===============================
在.NET Framework 中的大多數類型是引用類型。引用類型提供一個極大的靈活性,並傳遞他們時,他們提供出色的性能方法。以下各節討論公共內置類引入引用類型。第 4,課 介紹創建類、 接口和委託。
本課程後您將能夠:
1、分別解釋值類型和引用類型。
2、描述如何值類型和引用類型不同時分配值。
3、列出內置引用類型。
4、描述當您應使用 StringBuilder 類型。
5、創建和排序數組。
6、打開、讀、寫和關閉文件
檢測到異常發生和響應異常時。
預計需要40分鐘
引用類型是什麼?
引用類型將他們的數據也稱爲一個指針的地址存儲在堆棧上。實際地址指向的數據存儲在內存調用堆的區域中。運行庫管理,由通過一個稱爲垃圾回收的過程堆使用的內存。 垃圾回收恢復內存定期根據需要的處置不再引用的項目。
垃圾收集的最好方法
僅在需要時或觸發對 GC.Collect 的調用時,會發生垃圾回收。 自動垃圾回收爲實例最多有短暫除外分配的應用程序開頭的應用程序進行了優化。 以下的設計模式會最好的性能
比較引用和值類型的行爲
將引用類型表示的數據而不是數據本身地址,因爲將一個引用變量分配給另一個不是數據複製。 而,只將引用變量分配給另一個實例創建引用它是指相同的內存位置在堆上了原始變量的第二個副本。
請考慮下面的簡單結構聲明:
struct Numbers
{
  public int val;
  public Numbers(int _val)
  { val = _val; }
  public override string ToString()
  { return val.ToString(); }
}
現在考慮以下代碼創建數字結構的實例,將複製到另一個實例的結構修改兩個值,並顯示結果。
Numbers n1 = new Numbers(0);
Numbers n2 = n1;
n1.val += 1;
n2.val += 2;
Console.WriteLine("n1 = {0}, n2 = {1}", n1, n2);
此代碼會顯示 “ n1 = 1 n2 = 2 ” 因爲一個結構是種值,並複製值類型結果在兩個不同的值。
但是,如果您到類從一個結構更改數字類型聲明相同的應用程序會顯示 “ n1 = 3 n2 = 3 ”。 到類從結構更改數字會導致要引用鍵入而不是值類型。 在修改引用類型時您將修改該引用類型的所有副本。
內置引用類型
在.NET Framework中有大約2500個內置引用類。不是從 System.ValueType 派生的是引用類型,包括這些 2500 或因此內置引用類型。表 1-3 列出了從的很多其他引用類型派生,最常用的類型。
Table 1-3常見的引用類型
System.Object:Object 類型是 Framework 中的最通用類型。您可以將任何類型轉換爲 System.Object,並可以依靠從此類型繼承的 ToString、 GetType 和 Equals 成員任何類型。
System.String:文本數據。
System.Text.StringBuilder:動態文本數據。
System.Array:數組的數據。 這是所有數組的基類。數組聲明使用特定於語言的數組語法。
System.IO.Stream:文件、 設備,和網絡 I/O 的緩衝區。 這是一個抽象基類 ; 特定任務類均派生從流。
System.Exception:處理系統和應用程序定義的異常。特定任務的異常繼承此類型。
字符串和字符串生成器
類型不僅僅數據的容器,他們也提供操作通過其成員的數據的方法。 System.String 提供一組的成員
使用文本。 例如下面的代碼並快速搜索,並替換:
// C#
string s = "this is some text to search";
s = s.Replace("search", "replace");
Console.WriteLine(s);
類型爲 System.String 的字符串是不可變在.NET 中的。 這意味着字符串的任何更改使運行庫創建一個新的字符串和放棄舊。 發生這種情況,並且多程序員可能詫異下面的代碼分配內存中的四個新字符串:
// C#
string s;
s = "wombat"; // "wombat"
s += " kangaroo"; // "wombat kangaroo"
s += " wallaby"; // "wombat kangaroo wallaby"
s += " koala"; // "wombat kangaroo wallaby koala"
Console.WriteLine(s)
    只有最後一個被引用,其它三個都被自動垃圾回收。避免這些類型的臨時字符串有助於避免不必要的垃圾集合,可以提高性能。 有多種,避免臨時字符串方法:
1、使用 String 類的 String.Concat、 加入或格式方法在單個語句中加入多個項目。
2、使用StringBuilder創建一個動態文本。
StringBuilder 解決方案是最靈活的因爲它可以跨越多個語句。默認的構造函數會根據需要創建一個長度爲16字節的緩衝區。您如果您喜歡可以指定的初始大小和最大大小。下面的代碼演示使用StringBuilder:
// C#
System.Text.StringBuilder sb = new System.Text.StringBuilder(30);
sb.Append("wombat"); // Build string.
sb.Append(" kangaroo");
sb.Append(" wallaby");
sb.Append(" koala");
string s = sb.ToString(); // Copy result to string.
Console.WriteLine(s);
String 類的另一個細微但重要功能是它重寫從 System.Object 的運算符。 表 1-4 列出操作 String 類重寫。
Table 1-4 String Operators
加法   +  將兩個文本連接創建一個新的文本對象。
等於   == 如果兩個文本串相同返回真值,不同則返回假。
不等於 != 相等運算符的逆。
賦值   =  將一個字符串的內容複製到一個新。 這將導致像值類型的字符串,即使它們實施作爲引用類型。
如何創建和排序數組
作爲聲明的一部分使用括號 (在 Visual Basic 中) 或平方米的大括號 (在 C# 中) 聲明數組。正如 String System.Array 所提供成員用於處理其包含的數據。下面的代碼聲明一些初步的數組數據,然後排序數組。
// C#
// Declare and initialize an array.
int[] ar = { 3, 1, 2 };
// Call a shared/static array method.
Array.Sort(ar);
// Display the result.
Console.WriteLine("{0}, {1}, {2}", ar[0], ar[1], ar[2]);
如何使用流
流是另一種非常普遍的類型,因爲他們是用於從讀取和寫入磁盤並通過網絡通信手段。System.IO.Stream 類型是所有特定任務的流類型的基類型。表 1-5 顯示了一些最常用的流類型。在另外中找到網絡流,
System.Security.Cryptography 命名空間中找到 System.Network.Sockets 命名空間和加密的流。
Table 1-5 Common Stream Types
FileStream:創建一個基本流來讀寫文件。
MemoryStream:在內存中創建一個基本流進行讀寫。
StreamReader: 從流裏讀取數據。
StreamWriter: 從流裏寫數據。
最簡單的流類是 StreamReader 和 StreamWriter,使您能夠讀取和寫入文本文件。作爲構造函數使您可以用一行代碼打開一個文件的部分,可以傳遞一個文件名。您已處理一個文件後調用 Close 方法,以便該文件並不保持鎖定。下面的代碼需要 System.IO 命名空間演示如何寫入和讀取一個文本文件:
// C#
// Create and write to a text file
StreamWriter sw = new StreamWriter("text.txt");
sw.WriteLine("Hello, World!");
sw.Close();
// Read and display a text file
StreamReader sr = new StreamReader("text.txt");
Console.WriteLine(sr.ReadToEnd());
sr.Close();
更多信息流
關於更多的數據流在第二章的, “Input/Output (I/O).”
如何引發和捕獲異常
異常是中斷正常執行的程序集的意外的事件。例如如果您的程序集從可移動磁盤讀取一個大型的文本文件,並且用戶刪除磁盤運行庫將引發異常。這個意思是無法繼續運行。
異常應該永遠不會導致程序集完全失敗。而,您應計劃發生、 捕捉他們,和響應事件的異常。在前面的示例可以通知用戶該文件沒有,然後等候進一步從用戶的說明。以下簡化的需要代碼,System.IO 命名空間演示了這一點:
// C#
try
{
  StreamReader sr = new StreamReader(@"C:/boot.ini");
  Console.WriteLine(sr.ReadToEnd());
}
catch (Exception ex)
{
  // If there are any problems reading the file, display an error message
  Console.WriteLine("Error reading file: " + ex.Message);
}
   在前面的示例的任何類型的錯誤發生時 — 包括文件未找到錯誤、 足夠的權限錯誤文件的讀取期間 — Catch 塊內繼續處理。 如果不出現任何問題,運行庫將跳過 Catch 塊。
   基類 Exception 類非常有用,幷包含一條錯誤消息和其他應用程序數據。除了,異常基類,Framework 定義了數以百計的描述不同類型的事件從所有派生的異常類 System.SystemException。此外,您可以定義您自己的異常時您描述在更詳細的標準異常類允許通過從 System.ApplicationException 派生以外一項活動需要。
有多個異常類允許您以不同的方式迴應不同類型的錯誤。運行庫將執行只第一個 Catch 塊與一個匹配的異常類型,因此Catch順序最好是從最明確錯誤到不明確的錯誤,逐漸淺化。下面的代碼示例顯示錯誤、 出錯足夠的權限和任何其他類型的可能發生的錯誤找不到一個文件的不同的錯誤消息:
//c#
try
{
  StreamReader sr = New StreamReader("text.txt");
  Console.WriteLine(sr.ReadToEnd);
}
catch(System.IO.FileNotFoundException ex)
{
  Console.WriteLine("The file could not be found.");
}
catch(System.UnauthorizedAccessException ex)
{
  Console.WriteLine("You do not have sufficient permissions.");
}
catch(Exception ex)
{
  Console.WriteLine("Error reading file: " + ex.Message);
}
此過程有時稱爲篩選的異常。 異常處理也支持一個 Finally 塊。在 Finally 塊運行 Try 塊和任何 Catch 塊完成執行後, 是否引發異常。因此如果一個異常發生,您應使用一個 Finally 塊,關閉任何流或清理可能將保留任何其他打開對象。不論是否發生異常的對象下面的代碼示例關閉 StreamReader:
// C#
StreamReader sr = new StreamReader("text.txt");
try
{
  Console.WriteLine(sr.ReadToEnd());
}
catch (Exception ex)
{
  // If there are any problems reading the file, display an error message
  Console.WriteLine("Error reading file: " + ex.Message);
}
finally
{
  // Close the StreamReader, whether or not an exception occurred
  sr.Close();
}
   請注意在前面的示例在 Try 塊外提出 StreamReader 聲明。這是必要的因爲在 Finally 塊無法訪問在 Try 塊中聲明的變量。這使意義,因爲取決於異常的發生位置在 Try 塊中的變量聲明可能沒有執行。若要捕捉異常發生期間及之後 StreamReader 聲明,使用嵌套 Try/Catch/Finally 塊。
   通常,除爲簡單的變量聲明的所有代碼應都出現在 Try 塊中。可靠的錯誤處理可改進用戶體驗時問題發生,大大簡化了調試問題。但是,異常處理並會導致稍許的性能下降。節省空間和集中的特定主題,
這本書內的示例代碼通常不會包括異常處理。
實驗室: 使用引用類型
以下練習加強引用類型、 字符串和異常的知識。如果您遇到完成工作的問題,已完成的項目可用代碼文件夾中附帶 CD 上。
行使 1: 標識作爲值或引用類型
在這次您將編寫一個控制檯應用程序,顯示是否對象是值類型還是引用類型。
1.使用 Visual Studio 創建新的控制檯應用程序項目。 該項目名稱List-Value-Types.
2.創建以下類的實例:
❑ SByte
❑ Byte
❑ Int16
❑ Int32
❑ Int64
❑ String
❑ Exception
以下代碼演示了這一點:
// C#
SByte a = 0;
Byte b = 0;
Int16 c = 0;
Int32 d = 0;
Int64 e = 0;
string s = "";
Exception ex = new Exception();
3.添加各個實例到一個新的對象數組如下下面的代碼演示:
// C#
object[] types = { a, b, c, d, e, s, ex };
4.在一個 Foreach 循環循環檢查 object.GetType ().IsValueType 屬性確定類型是否是值類型。 顯示每個鍵入名稱和是否值類型或引用鍵入,下面的代碼演示:
// C#
foreach ( object o in types )
{
  string type;
  if (o.GetType().IsValueType)
  type = "Value type";
  else
  type = "Reference Type";
  Console.WriteLine("{0}: {1}", o.GetType(), type );
}
5.運行控制檯應用程序並確認每個類型匹配您瞭解。
行使 2: 使用字符串和數組
在這次您將編寫一個排序字符串的函數
1.使用 Visual Studio 創建新的控制檯應用程序項目。 該項目名稱SortString。
2.定義一個字符串。 然後,使用 String.Split 方法字符串分成單詞的數組。 以下代碼演示了這一點:
// C#
string s = "Microsoft .NET Framework 2.0 Application Development Foundation";
string[] sa = s.Split(' ');
3.按下面的代碼演示調用 Array.Sort 方法來對單詞,數組進行排序:
// C#
Array.Sort(sa);
4.調用 String.Join 方法將數組的單詞迴轉換單個字符串,然後將字符串寫入控制檯。 下面的代碼示例演示此:
// C#
s = string.Join(" ", sa);
Console.WriteLine(s);
5.運行控制檯應用程序並確認正常工作。
行使 3: 使用流和異常
請考慮在其中一個同事編寫一個簡單的 Windows 窗體應用程序,以查看文本文件的情況。 但是,用戶投訴是非常多。 如果用戶輸入錯誤文件名或如果文件不可用因任何理由,應用程序失敗並出現未處理的異常錯誤。 如果文件不可用您必須添加異常處理應用程序向用戶顯示友好錯誤信息。
將 1 Chapter01/Lesson2 ViewFile 文件夾從本書 CD 複製到您的硬盤上並打開 C# 版本或 Visual Basic.NET 版本的ViewFile 項目。
2.當用戶嘗試查看文件會發生異常。因此,編輯代碼運行 showButton.Click 事件。添加代碼以捕獲任何類型的異常發生,並在向用戶一個對話框中顯示錯誤信息。如果發生異常的 TextReader 對象初始化後,您應關閉它是否一個發生異常。您需要兩個嵌套的 Try 塊: 一個 TextReader 在初始化期間捕捉異常,第二個一個以讀取該文件時捕獲異常。 下面的代碼示例演示此:
// C#
try
{
  TextReader tr = new StreamReader(locationTextBox.Text);
  try
  { displayTextBox.Text = tr.ReadToEnd(); }
  catch (Exception ex)
  { MessageBox.Show(ex.Message); }
  finally
  { tr.Close(); }
}
catch (Exception ex)
{ MessageBox.Show(ex.Message); }
3.運行應用程序。 先驗證成功可以顯示一個文本文件。 然後提供一個無效的文件名,並驗證提供文件名無效時出現一個消息框。
4.接下來添加重載的異常處理捕捉 System.IO.FileNotFoundException 和 System.UnauthorizedAccessException。 下面的代碼示例演示此:
// C#
try
{
  TextReader tr = new StreamReader(locationTextBox.Text);
  try
  { displayTextBox.Text = tr.ReadToEnd(); }
  catch (Exception ex)
  { MessageBox.Show(ex.Message); }
  finally
  { tr.Close(); }
}
catch (System.IO.FileNotFoundException ex)
{ MessageBox.Show("Sorry, the file does not exist."); }
catch (System.UnauthorizedAccessException ex)
{ MessageBox.Show("Sorry, you lack sufficient privileges."); }
catch (Exception ex)
{ MessageBox.Show(ex.Message); }
5.再次運行應用程序,並驗證它提供新的錯誤信息,是否提供了一個無效的文件名。
本課摘要
■ 引用類型包含數據,而非實際數據地址。
■ 如果您複製值類型,被創建第二個副本的值。 當您複製引用類型時,被複制只指針。 因此,如果您複製的引用鍵入,然後修改該副本副本和原始變量被更改。
■ 的.NET Framework 包括大量的內置引用類型可以直接使用或使用構建自己的自定義類型。
■ 字符串是不可變的 ; 使用 StringBuilder 類來動態創建一個字符串。
■ 使用流讀取和寫入文件、 內存和網絡。
■ 用於 Catch 子句在 Try 塊中的篩選由類型的異常。 關閉並釋放 nonmemory 資源中,最後條 Try 塊。

===================================
========第三課  創建類   =================
===================================
在面嚮對象語言中大部份的工作應對象中執行。所有但最簡單的應用程序需要興建一或多個自定義類每多個屬性和用於執行任務的方法與
該對象。這一課討論如何創建自定義類。
本課程後您將能夠:
■描述並使用繼承。
■描述和使用接口。
■描述並使用分部類。
■創建一個泛型類型和使用內置的泛型類型。
■響應,並引發事件
■添加屬性以描述程序集和方法
■移動到另一個從一個類庫類型使用類型轉發。
預計課時40分鐘
什麼是繼承?
.NET Framework 數以千計的類,並每個類有許多不同的方法和屬性。跟蹤所有這些類和成員的不可能如果.NET Framework 不實現非常一致。例如每個類具有一個 ToString 方法,執行完全相同的任務 — 類的一個實例轉換爲字符串。同樣地,許多類支持同一操作如比較兩個實例相等的類。
此一致性是可能因爲繼承和接口 (如下一節所述)。從現有的類中使用繼承來創建新的類。如您將瞭解第 6 章 “ 圖形,” Bitmap 類從 Image 類繼承,並通過添加功能擴展中。因此,您可以使用 Bitmap 類的實例您會使用 Image 類的實例以相同方式。但是,Bitmap 類提供了其他方法,使您進行更多的圖片處理和操作。您可以輕鬆創建一個自定義異常處理類通過繼承 System.Application-異常,如下所示:
// C#
class DerivedException : System.ApplicationException
{
  public override string Message
  {
    get { return "An error occurred in the application."; }
  }
}
您可以引發和捕捉新異常,因爲自定義類繼承其基類的行爲,如下所示:
// C#
try
{
  throw new DerivedException();
}
catch (DerivedException ex)
{
  Console.WriteLine("Source: {0}, Error: {1}", ex.Source, ex.Message);
}
從 System.Application 異常繼承自定義異常不僅支持該 throw/catch 行爲,但它還包括源成員 (以及其他) 的通知。
繼承的另一個好處是能夠互換使用派生的類。例如有 System.Drawing.Brush 從基類繼承的五個類:HatchBrush, LinearGradientBrush, PathGradientBrush, SolidBrush,和 TextureBrush。Graphics.DrawRectangle 方法需要 Brush 對象作爲其參數之一 ;但是,您會將永遠不會通過 Brush 基類轉換爲 Graphics.DrawRectangle。 而且,您將傳遞派生類之一。 因爲每個從 Brush 類派生他們,aphics.DrawRectangle 方法可以接受任何人。 同樣,如果您要創建一個從 Brush 類派生的自定義類,您可能還將該類傳遞給 Graphics.DrawRectangle。
什麼是接口?
也稱爲合約,接口定義成員的實現接口的所有類必須都提供的常見的集。例如 IComparable 接口定義 CompareTo 方法,使兩個實例相等比較類。實現 IComparable 接口接口的所有類創建自定義-還是興建,可以進行都比較相等。
IDisposable 是類的提供單個方法 Dispose 以便創建您釋放該實例佔用了任何資源的實例的程序集的接口。若要提供一個實現 IDisposable 接口使用 Visual Studio 2005 類請按照下列步驟操作:
1.創建類聲明。 例如:
// C#
class BigClass
{
}
2.添加接口聲明。 例如:
// C#
class BigClass : IDisposable
{
}
3.如果您使用 Visual Basic,Visual Studio 應該自動生成方法聲明爲每個所需的方法。 如果它不刪除該
實現命令,然後重試 ;Visual Studio 可能仍在啓動。 如果您使用 C#,右鍵單擊接口聲明,單擊實現接口然後再單擊實現接口,圖 1-1 所示。
4.爲每個接口的方法編寫代碼。 在此示例中,您會編寫在 Dispose 方法釋放已分配任何資源中的代碼。
表 1-6 列表,通常用於在.NET Framework 中的接口。
IComparable:實施由其值可以進行排序的類型 ;例如,數字和字符串類。 IComparable 是所需排序。
IDisposable:定義方法手動釋放的對象。 這接口是重要的消耗的大型對象資源或鎖定訪問的對象如數據庫的資源。
IConvertible:啓用要轉換爲一個基礎類如 Boolean Byte,Double,鍵入或 String。
ICloneable:複製的對象的支持。
IEquatable:允許您與平等類的實例進行比較。 例如如果要實現此接口,可以說 “ 如果 (a = = b) ”。
IFormattable:使您能夠對象的值轉換爲特殊格式的字符串。 這提供了,比基本的 ToString 方法更靈活
您可以創建您自己接口。這很有用如果您要創建多個自定義類同樣的行爲,可互換使用。 例如下面的代碼定義包含三個成員的接口:
// C#
interface IMessage
{
  // Send the message. Returns True is success, False otherwise.
  bool Send();
  // The message to send.
  string Message { get; set; }
  // The Address to send to.
  string Address { get; set; }
}
如果您在一個新類中實現該接口,Visual Studio 將爲接口成員生成下列模板:
class EmailMessage : IMessage
{
  public bool Send()
  {
    throw new Exception("The method or operation is not implemented.");
  }
  public string Message
  {
    get
   {
     throw new Exception("The method or operation is not implemented.");
   }
    set
   {
     throw new Exception("The method or operation is not implemented.");
   }
  }
  public string Address
  {
    get
    {
     throw new Exception("The method or operation is not implemented.");
    }
    set
    {
     throw new Exception("The method or operation is not implemented.");
    }
   }
}
如果您創建自定義類,並且以後決定會很有用具有相同的成員的多個類,VisualStudio 有接口摘錄自定義類的快捷方式。 只需請按照這些步驟操作:
1.右擊 Visual Studio 2005 中的類。
2.單擊重構,然後單擊提取接口。
3.指定接口名稱,選擇公共成員,相應窗體,接口,然後單擊確定。
類可以實現多個接口。 因此,一個類可以實現 IComparable 和 IDisposable 接口。
什麼是分部類?
注意 .NET 2.0 分佈類是.net2.0新概念
    分部類允許您跨多個源文件拆分類定義。此方法的好處是它隱藏類定義的詳細信息,以便派生的類可以專注於更重要的部分。
    Windows 窗體類是一個內置的分部類的示例。在 Visual Studio 2003 中和較早前,窗體類包含由窗體設計器生成代碼。現在在一個名爲 form.Designer.vb 或 form.Designer.cs 的分部類中是隱藏的代碼。
在 Visual Basic 中必須選擇在 SolutionExplorer@@ 可以查看分部類文件顯示所有文件。在 C#,視圖默認啓用的。分部類不是考試目標的一部分,但您需要知道它們以便您可以找到該窗體當您創建一個新的 Windows 窗體的設計器代碼的時候。
什麼是泛型?What Are Generics?
泛型是指在類或者方法包含類型參數,從而可以使用同一個類或方法能夠作用於多種不同的類型。而不是指定類型的參數或成員類,您可以使用您的類型指定它的代碼。這允許使用者代碼定製您自己的具體需求的類型。
考試提示:由於泛型是.net2.0新添的內容,因此在考試中關於泛型會出現大量的試題。
.NET Framework 2.0 版中,System.Collections 包括幾個泛型類。泛型命名空間包括 Dictionary、 Queue、 SortedDictionary 和 SortedList。這些類工作同樣非泛型接口 System.Collections,但它們提供改進的性能和類型安全。
更多信息 一般集合
.NET Framework 2.0 版包括提供了 System.Collections.Generic 命名空間提供改進的性能標準集合的內置集合。 有關更多信息請參閱第 4 “ 集合和泛型 ”。
爲什麼要用泛型呢?
1.0 和 1.1 版的.NET Framework 不支持泛型。而開發人員 Object 類用於參數和成員,並會轉換至 Object 類的其他類。 泛型提供通過使用 Object 類的兩個重要優點:
■ 降低運行時錯誤:您強制轉換時,編譯器無法檢測類型錯誤至 Object 類。例如如果轉換一個字符串,Object 類
然後嘗試強制轉換爲整數的對象,編譯器不會捕獲錯誤。 而,運行庫將引發異常。 使用泛型允許,
編譯器將捕捉此類型的錯誤之前程序運行。 此外,您可以指定約束,以限制在一個泛型中使用該類啓用編譯器檢測到不兼容的類型。
■ 改善性能:需要裝箱和取消裝箱 (稍後解釋在 Lesson 4 “ 轉換 Between Types ”) 的 steals 處理器時間並降低性能。 使用泛型 doesn’t 需要轉換或裝箱提高了運行時性能。
怎樣創建泛型類?
首先,研究以下類。 類對象和第代執行完全相同任務,但是對象使用 Object 類來啓用任何類型必須通過第代使用泛型時:
// C#
class Obj
{
  public Object t;
  public Object u;
  public Obj(Object _t, Object _u)
  {
   t = _t;
   u = _u;
  }
}
class Gen<T, U>
{
  public T t;
  public U u;
  public Gen(T _t, U _u)
  {
   t = _t;
   u = _u;
  }
}
   你可以看到,對象類有兩個對象成員類型,這個gen類有兩個成員分別是T類型和U類型。耗費大量代碼來確定T和U的類型,T和U可以是字符串、整形、一個自定義類或任何組合。
創建泛型類還有一個重大限制:泛型代碼如果纔會有效它將編譯爲每個可能興建的泛型實例是否一個 int 一個字符串或任何其他類。因此,您可以在您的類調用 ToString 或 GetHashCode 方法,但您不能使用+,或 > 運算符。這些相同的限制不適用於佔用代碼因爲耗費大量代碼已聲明爲泛型類型。
如何使用泛型類型
當您使用泛型類型時,您必須指定使用任何泛型的類型。考慮以下控制檯應用程序代碼,它會使用GEN和對象類:
// C#
// Add two strings using the Obj class
Obj oa = new Obj("Hello, ", "World!");
Console.WriteLine((string)oa.t + (string)oa.u);
// Add two strings using the Gen class
Gen<string, string> ga = new Gen<string, string>("Hello, ", "World!");
Console.WriteLine(ga.t + ga.u);
// Add a double and an int using the Obj class
Obj ob = new Obj(10.125, 2005);
Console.WriteLine((double)ob.t + (int)ob.u);
// Add a double and an int using the Gen class
Gen<double, int> gb = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gb.t + gb.u);
如果您在一個控制檯應用程序中運行該代碼,obj和gen類產生完全相同的結果。但是,使用gen類的代碼實際可更快,因爲它不需要裝箱和取消裝箱至 Object 類。另外,開發人員必須使用gen類很容易。首先,開發人員會不要手動從 Object 類轉換爲相應的類型。第二,在編譯時 (而不是在運行時),會被捕獲類型錯誤。若要演示的好處,請考慮下面的代碼包含一個錯誤.
// C#
// Add a double and an int using the Gen class
Gen<double, int> gc = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gc.t + gc.u);
// Add a double and an int using the Obj class
Obj oc = new Obj(10.125, 2005);
Console.WriteLine((int)oc.t + (int)oc.u);
最後一行在該代碼示例包含一個錯誤 — oc.t 值轉換到一個 double 而不是一個 int。可惜,編譯器不能捕捉該錯誤。當而,在 C# 中運行時異常時將引發運行時嘗試轉換爲 int 值一個 double。 更易於修復的錯誤,編譯器捕獲和很難檢測並修復一個運行時錯誤,因此泛型類提供一個明確的好處。
如何使用約束
如果您可以只編寫會編譯的任何類的代碼,因爲您會限於對象基類的功能,泛型會極爲有限。若要克服此限制,使用約束來放佔用代碼可以替換爲您泛型類型的要求。
泛型支持四種類型的約束:
■ 接口:允許僅實現特定接口使用您泛型的類型。
■ 基類:允許僅匹配或使用您泛型特定從基類繼承的類型。
■構造函數:需要使用您泛型實現無參數構造函數的類型。
■引用或值類型:需要使用您泛型來引用或值類型的類型。
在 Visual Basic 中使用 As 子句或 where 子句 C# 約束應用於泛型。 例如以下泛型類只能是實現 IComparable 接口:
// C#
class CompGen<T>
  where T : IComparable
{
  public T t1;
  public T t2;
  public CompGen(T _t1, T _t2)
  {
   t1 = _t1;
   t2 = _t2;
  }
  public T Max()
  {
   if (t2.CompareTo(t1) < 0)
   return t1;
   else
   return t2;
   }
}
上述類將正確編譯。 但是,如果您刪除 where 子句,編譯器將返回錯誤指示不包含泛型類型 T 中的CompareTo 定義。通過約束泛型實現的類
IComparable,您保證 CompareTo 方法將始終可用。
事件
大多數項目是非線性的。在 Windows 窗體應用程序中您可能要等待用戶可以單擊一個按鈕或按一個鍵,然後響應該事件。在服務器應用程序中您可能要等待傳入的網絡請求。 這些功能是由.NET Framework事件提供的以下各節所述。
什麼是事件?
事件是操作的一個由對象發送信號發生的消息。操作可能是由用戶交互如單擊鼠標或導致的一些其他程序邏輯觸發。引發事件的對象稱爲事件發件人。捕獲事件並響應,該對象稱爲事件接收器。
在事件通信中事件發件人類不知道哪些對象或方法將接收 (句柄) 它引發的事件。需要的是中介(或指針類似的機制) 源和接收方之間。.NET Framework 定義一個特別 (Delegate) 的類型提供的函數指針的功能。
委託是什麼?
委託是一個類,可以保存對方法的引用。 與其他類委託類具有一個簽名,它可以保存只對與其簽名匹配的方法的引用。委託是因此等效於一個類型安全函數指針或一個回調。委託有其它用途,在此討論的重點委託的事件處理功能。委託聲明是足以定義一個委託類。聲明供應該委託的簽名和公共語言運行庫提供實現。下面的示例演示一個事件委託聲明:
// C#
public delegate void AlarmEventHandler(object sender, EventArgs e);
標準的事件處理程序委託簽名定義並不返回一個值是其第一個參數是 Object 類型,引用實例引發該事件,並且從類型 EventArgs 派生其第二個參數,保留事件數據的方法。如果該事件不會生成事件數據,第二個參數是 EventArgs 的簡單實例。否則,第二個參數是一個自定義的類型從 EventArgs 派生和提供任何字段或保留事件數據所需的屬性。
EventHandler 是一個預定義的委託,明確表示不生成數據的事件的事件處理程序方法。如果您的事件生成數據,您必須提供您自己的自定義事件數據類型,或者創建一個委託類型的第二個參數是您自定義的類型或您必須使用泛型的 EventHandler 委託類並替換爲泛型類型參數自定義類型。
若要將事件與將處理該事件方法,將委託的一個實例添加到事件。每次發生該事件時,調用事件處理程序除非您刪除該委託。
如何響應事件
您必須執行兩件事以響應的事件:
■ 創建一個方法以響應事件。 該方法必須與委託簽名匹配。通常,這意味着它必須返回 void,並接受兩個參數: 一個和 EventArgs (或派生的類)。 以下代碼演示了這一點:
// C#
private void button1_Click(object sender, EventArgs e)
{
  // Method code
}
■ 添加事件處理程序表明哪種方法應接收事件,因爲下面的代碼演示:
// C#
this.button1.Click += new System.EventHandler(this.button1_Click);
NOTE .NET 2.0
.NET Framework 2.0 包括一個新的泛型版本,EventHandler 的類型。
當事件發生時, 將運行您指定的方法。
如何引發事件
至少一個你必須要引發事件三件事:
■ 創建委託:
// C#
public delegate void MyEventHandler(object sender, EventArgs e);
■ 創建一個事件成員:
// C#
public event MyEventHandler MyEvent;
■ 調用委託方法內的,在需要時引發該事件按以下代碼演示:
// C#
MyEventHandler handler = MyEvent;
EventArgs e = new EventArgs();
if (handler != null)
{
  // Invokes the delegates.
  handler(this, e);
}
此外,您可以從 EventArgs 派生自定義類,如果您要傳遞到事件處理程序的信息。
屬性是什麼?

屬性描述類型、 方法或屬性可以使用稱爲反射的方法以編程方式查詢的方式。屬性一些常見用途是:
■ 指定一類要求的安全權限
■ 指定安全權限拒絕降低安全風險
■ Declare 功能 如支持序列化
■ 請通過提供一個標題、 說明和版權通知從系統派生的屬性類型的程序集。使用 < > 或[] 屬性基類,指定表示法。 下面的代碼示例演示如何添加程序集屬性:
// C# - AssemblyInfo.cs
[assembly: AssemblyTitle("ch01cs")]
[assembly: AssemblyDescription("Chapter 1 Samples")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft Learning")]
[assembly: AssemblyProduct("ch01cs")]
[assembly: AssemblyCopyright("Copyright © 2006")]
[assembly: AssemblyTrademark("")]
Visual Studio 自動創建一些標準屬性爲程序集創建一個項目包括標題、 說明、 公司、 指南和版本時。您應編輯這些屬性爲您創建,因爲默認情況下,不包括重要信息如說明每個項目。屬性超過描述要其他開發人員的程序集,他們還可以聲明要求或功能。要啓用一個類來進行序列化示例
您必須添加 Serializable 屬性,如以下代碼所示:
// C#
[Serializable]
class ShoppingCartItem
{
}
沒有 Serializable 屬性類是不可序列化。同樣,下面的代碼使用屬性來聲明它需要讀取 C:/boot.ini 文件。 由於在執行之前,如果沒有指定的權限此屬性的運行庫將引發異常,在執行之前,如果沒有指定的權限:
// C#
using System;
using System.Security.Permissions;
[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@"C:/boot.ini")]
namespace DeclarativeExample
{
 class Class1
 {
 [STAThread]
 static void Main(string[] args)
 {
  Console.WriteLine("Hello, World!");
 }
 }
}
什麼是類型轉發?
類型轉發是使您可以將一種類型從一個程序集 (程序集) 移動到另一個程序集 (程序 B 集),並做到這一種,它不需要重新編譯使用的客戶端的屬性 (在實現 TypeForwardedTo)程序集 A。組件 (程序集) , 所使用的客戶端應用程序,您可以使用類型轉發移動類型從組件 (程序集) 到另一個程序集船舶更新的組件 (和所需的任何其他程序集) 和客戶端應用程序仍能不被重新編譯。轉發工程只由現有應用程序引用的組件的類型。當您重新生成應用程序時,必須應用程序中使用任何類型的相應的程序集引用。
要將一個類型從一類庫移動到另,請按照這些步驟操作:
類型轉發是.NET 2.0 中的新功能。
1.將 TypeForwardedTo 屬性添加到源類庫程序集。
2.剪切源類庫中的類型定義。
3.將粘貼到目標類庫的類型定義。
4.重新生成這兩個庫。
下面的代碼演示用於將 TypeA 移動到 DestLib 類庫屬性聲明:
// C#
using System.Runtime.CompilerServices;
[assembly:TypeForwardedTo(typeof(DestLib.TypeA))]
實驗室: 使用委託創建派生的類Lab: Create a Derived Class with Delegates
--------------------------------49

在本練習您將從您在第 1 課中創建 Person 類派生新類。
1、將Chapter01/Lesson3-Person文件夾從 CD 複製到您的硬盤上並打開 C# 版本或 Visual Basic 版本的 CreateStruct項目。
2、將Person由結構改變成類
3、創建一個新類命名爲Manager 它繼承自基類Person
// C#
class Manager : Person
{
}
4、增加兩個新的公共成員分別是strings: phoneNumber 和 officeLocation。
5、覆載該構造函數接受一個phone和辦公地址來定義新成員。 您需要將調用基類的構造函數,如下所示下面的代碼示例:
// C#
public Manager(string _firstName, string _lastName, int _age,
Genders _gender, string _phoneNumber, string _officeLocation)
: base (_firstName, _lastName, _age, _gender)
{
phoneNumber = _phoneNumber;
officeLocation = _officeLocation;
}
6、覆載ToString方法,增加phone和office location 代碼如下所示:
// C#
public override string ToString()
{
return base.ToString() + ", " + phoneNumber + ", " + officeLocation;
}
7、修改Main方法,創建一個Manager繼承於person對象,然後運行你的程序看看是否達到目的。
實驗2: 響應事件
在本次實驗,你將創建一個類來響應timer事件。
1、通過使用Visual Studio,創建一個Windows窗體應用項目,項目的名字爲TimerEvents。
2、在窗體添加ProgressBar控件。
3、在窗體類,聲明一個 System.Windows.Forms.Timer 對象的實例。對象可用於指定毫秒間隔後引發事件。下面的代碼生命一個Timer對象。
// C#
System.Windows.Forms.Timer t;
4、在設計器中,查看窗體屬性。然後查看事件的列表。雙擊 Load 事件以自動創建一個事件處理程序在窗體在第一次運行時候。在該方法初始化該Timer對象時候,將間隔設置爲一秒,創建一個事件處理程序,Tick 事件並開始計時。如下代碼所示:
// C#
private void Timer_Shown(object sender, EventArgs e)
{
t = new System.Windows.Forms.Timer();
t.Interval = 1000;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
5、實現方法,將會對 Timer.Tick事件作出迴應。當事件發生時, 向 ProgressBar.Value 屬性添加 10。當ProgressBar.Value值爲100的時候停止計時。如下代碼所示:
// C#
void t_Tick(object sender, EventArgs e)
{
progressBar.Value += 10;
if (progressBar.Value >= 100)
t.Stop();
}
6、運行應用程序以驗證它響應每秒計時器事件。
本課概要:
■運用繼承從一個基類獲得繼承。
■使用接口定義一組通用必須由相關類型實現的成員。
■分部類拆分成多個源文件的類定義。
■事件,可以是不同的代碼部分中發生時運行指定的方法。
■使用屬性來描述程序集、 類型和成員。
■若要將一個類型從一個類庫移動到另使用 TypeForwardedTo 屬性。

第 4 課: 類型之間轉換

你經常會遇到類型轉換的問題。舉個例子,您可能需要確定Integer是否大於或

 

小於 Double。您可能需要將 Double 傳遞給需要Integer作爲參數的方法。或

 

者,您可能需要一個數字顯示爲文本。本課介紹如何在兩個 Visual Basic 中

 

的類型和 C# 之間轉換。 類型轉換是 Visual Basic 和 C# 凡顯著不同,幾個

 

領域之一。

本課程後您將能夠:

■類型之間的轉換

■應避免裝箱

■實現運算符轉換

估計課時間: 20 分鐘

在 Visual Basic 和 C# 中的轉換

默認情況下,Visual Basic 允許而 C# 禁止失去精度的隱式轉換的類型之間的

 

隱式轉換。

Visual Basic 和 C# 允許隱式轉換,如果目標類型可容納所有可能的值從源類

 

型。 被稱爲一個擴大轉換,它說明了下面的示例:

// C#

int i = 1;

double d = 1.0001;

d = i; // 允許轉換.

如果區域或源類型的精度超過目標類型的操作稱爲一個收縮轉換通常需要顯式轉

 

換。

表 1-7 列出如何執行顯式轉換

System.Convert:System.IConvertible用來實現類型之間轉換。

type.ToString,type.Parse:字符串與基類型 ; 如果引發異常,將不能夠轉

 

換。

type.TryParse,type.TryParseExact :從基類型轉換爲字符串 ; 如果轉換

 

是不可能返回 false。

注意 .NET2.0

TryParse, TryParseExact, 和 TryCast是.NET2.0新增加內容。如果失敗您不

 

得不嘗試分析或轉換,然後捕獲異常。

收縮的轉換失敗如果源值超出了目標類型的範圍或類型之間轉換不是定義,因此

 

應將括中的收縮轉換 Try 塊或使用 TryCast 或 TryParse 並檢查返回值。

什麼是裝箱和拆箱

裝箱將值類型轉換爲引用類型,拆箱是將引用類型轉換爲值類型。下面的示例通

 

過將整數 (值類型) 轉換爲對象 (引用類型) 演示裝箱:

// C#

int i = 123;

object o = (object) i;

拆箱發生如果您爲值類型分配一個引用對象。 下面的示例演示取消裝箱:

// C#

object o = 123;

int i = (int) o;

裝箱和拆箱的最佳做法

裝箱和拆箱導致開銷,所以您應該避免他們編程認真重複性任務時。 當您調用

 

一個結構繼承自 System.Object 如 ToString 的虛擬方法時也會發生裝箱。 

 

請按照這些提示,以避免不必要的裝箱操作:

■實現特定於類型的版本 (重載) 接受各種值類型的程序。是比創建幾個重載

 

的過程接受一個對象參數的更好做法。

■儘可能使用泛型而不是接受 Object 參數

■定義結構時,重寫 ToString、 等於和 GetHash 虛擬成員。

如何在自定義類型中實現轉換

您可以爲自己定義類型寫類型轉換的方法。 您選擇的方法取決於要執行的轉換

 

的類型:

■定義以簡化收縮和擴大轉換數值類型之間的轉換運算符。

■重寫 ToString 提供轉換爲字符串,並覆蓋分析提供從字符串轉換。

■實現 System.IConvertible,使通過 System.Convert 轉換。使用此方法使

 

區域性特定轉換。

■實現 TypeConverter 類以便在 Visual Studio 屬性窗口中使用的設計時轉

 

換。設計時轉換超出範圍考試和 TypeConverter 類沒有涉及在這本書的情況。

設計時轉換更多信息。

注意 .NET 2.0

轉換運算符是.NET 2.0 中新增的。

定義轉換運算符使您可以直接從值類型分配給您自定義的類型。擴大隱關鍵字用

 

於不能失去精度 ;使用 Narrowing 明確的關鍵字 的轉換可能失去精度的轉換

 

例如以下結構定義允許分配至整數值的運算符:

// C#

struct TypeA

{

  public int Value;

  // Allows implicit conversion from an integer.

  public static implicit operator TypeA(int arg)

  {

    TypeA res = new TypeA();

    res.Value = arg;

    return res;

   }

   // Allows explicit conversion to an integer

  public static explicit operator int(TypeA arg)

  {

    return arg.Value;

  }

  // Provides string conversion (avoids boxing).

  public override string ToString()

  {

    return this.Value.ToString();

   }

}

上述類型還重寫 ToString 執行字符串轉換不裝箱。 現在可以將分配整數給該

 

類型直接,如下所示:

// C#

TypeA a; int i;

// Widening conversion is OK implicit.

a = 42; // Rather than a.Value = 42

// Narrowing conversion must be explicit.

i = (int)a; // Rather than i = a.Value

Console.WriteLine("a = {0}, i = {0}", a.ToString(), i.ToString());

若要實現 System.IConvertible 接口,添加 IConvertible 接口,該

類型定義。 然後,使用 Visual Studio 自動實現接口。Visual Studio 插入

 

成員聲明爲 17 方法包括每個基類型 Boolean.GetTypeCode、 ChangeType 和 

 

IConvertible.ToType 方法。不能實現每個方法和一些 — 如 ToDateTime — 

 

可能會無效。無效方法只是引發異常 — Visual Studio 將自動添加代碼以引

 

發異常爲您 不能實現任何轉換方法。

實現 IConvertible 後自定義的類型可以使用標準的 System.Convert 類,如

 

下所示轉換:

// C#

TypeA a; bool b;

a = 42;

// Convert using ToBoolean.

b = Convert.ToBoolean(a);

Console.WriteLine("a = {0}, b = {1}", a.ToString(), b.ToString());

實驗室: 安全地執行轉換

以下練習顯示如何避免隱式轉換問題,以便程序可預測函數。 如果您遇到完成

 

工作的問題,已完成的項目可用代碼文件夾中附帶 CD 上。

行使 1: 檢查隱式轉換

這次您會研究以確定哪些數字類型允許隱式轉換。

1.在 Visual Studio 中創建一個新控制檯應用程序。

2.聲明的三種值類型實例: Int 16 Int 32,和double。 下面的代碼示例演示

 

此:

// C#

Int16 i16 = 1;

Int32 i32 = 1;

double db = 1;

3.嘗試將每個變量分配給所有其他,如下面的代碼示例所示。

// C#

i16 = i32;

i16 = db;

i32 = i16;

i32 = db;

db = i16;

db = i32;

4.嘗試生成您的項目。 編譯器是否允許的隱式轉換,以及爲何?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章