注意,派生類的構造函數隱式調用了基類(或 Java 術語中的超類)的構造函數。在繼承中,所有的基類構造函數都是按照這些類出現在類層次中的順序在派生類的構造函數之前調用的。
將類型強制轉換到基類
與在 Java 中一樣,我們不能使用對基類的引用來訪問派生類的成員和方法,即使基類引用可能包含對派生類型對象的有效引用也同樣如此。
我們可以通過隱式地引用派生的類型來引用派生類:
ColorPoint clrpt = new ColorPoint();
Point pt = clrpt;
在這段代碼中,基類引用 pt 包含 clrpt 引用的副本。
base 關鍵字
通過使用 base 關鍵字,我們可以訪問子類中的基類成員,即使這些基類成員在超類中被重寫也同樣如此。例如,我們可以創建一個派生類,該類所包含的方法具有與基類中相同的簽名。如果我們在此方法前加上 new 關鍵字,就表示這是一個屬於派生類的全新方法。通過 base 關鍵字,我們仍然可以提供方法來訪問基類中的原始方法。
例如,我們的 Point 基類有名為 invert() 的方法,它交換 x 和 y 坐標。通過使用下面這樣的代碼,我們可以在派生類 ColorPoint 中提供此方法的替代方法:
public new void invert()
{
int holding = X;
X = Y;
Y = holding;
screenColor = Color.Gray;
}
正如您所見,該方法交換 x 和 y,然後將點的顏色設置為灰色。通過在 ColorPoint 中創建另一個方法(例如下面的這個方法),我們可以提供對此方法的基實現的訪問:
public void baseInvert()
{
base.invert();
}
然後,我們就可以通過調用 baseInvert() 方法來調用 ColorPoint 對象中的基方法。
ColorPoint clrpt = new ColorPoint();clrpt.baseInvert();
請記住,如果我們將對基類的引用賦值給 ColorPoint 的實例,然後訪問它的方法,我們將獲得相同的效果:
Point pt = clrpt;
pt.invert();
選擇構造函數
基類對象總是在任何派生類之前構造的。因此基類的構造函數在派生類的構造函數之前執行。如果基類有多個構造函數,派生類就可以決定要調用的構造函數。例如,我們可以修改我們的 Point 類來添加第二個構造函數:
public class Point
{
private int x, y;
public Point()
{
x = 0; y = 0;
}
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
然後,通過使用 base 關鍵字,我們可以將 ColorPoint 類更改為使用某個特定的可用構造函數:
public class ColorPoint : Point
{
private Color color;
public ColorPoint(int x, int y) : base (x, y)
{
color = Color.Red;
}
}
在 Java 中,這項功能是通過 super 關鍵字來實現的。
方法重寫
通過為聲明的方法提供新的實現,派生類可以重寫基類的方法。Java 和 C# 之間的一個重要區別在於,Java 方法在默認情況下標記為虛方法,而在 C# 中,必須使用 virtual 修飾符才能將方法顯式標記為虛方法。可以采用大致相同的方式重寫屬性訪問器以及方法。
虛方法
派生類中要被重寫的方法是用 virtual 修飾符聲明的。而在派生類中,已被重寫的方法用 override 修飾符聲明。
override 修飾符表示派生類的方法或屬性,這個派生類代替基類中具有相同的名稱和簽名的類。要被重寫的基方法必須聲明為 virtual、abstract 或 override:以這種方式重寫非虛方法或靜態方法是不可能的 — 請參見關於此問題的下一部分。已被重寫的方法或屬性和重寫方法或屬性必須具有相同的訪問級修飾符。
下面的示例顯示了一個稱為 StepUp 的虛方法,它是在派生類中用 override 修飾符重寫的:
using System;
public class CountClass
{
public int count;
// Constructor
public CountClass(int startValue)
{
count = startValue;
}
public virtual int StepUp()
{
return ++count;
}
}
class Count100Class : CountClass
{
// Constructor
public Count100Class(int x) : base(x)
{
}
public override int StepUp()
{
return ((base.count) + 100);
}
public static void Main()
{
CountClass counter = new CountClass(10);
CountClass bigCounter = new Count100Class(10);
Console.WriteLine("Value of count in base class = {0}",
counter.StepUp());
Console.WriteLine("Value of count in derived class = {0}",
bigCounter.StepUp());
}
}
當我們運行這段代碼時,會發現派生類的構造函數使用基類中給出的方法體,這使得我們在不復制該代碼的情況下就可以初始化 count 成員。下面是我們得到的輸出結果:
Value of count in base class = 11
Value of count in derived class = 110
抽象類
抽象類將一個(或多個)方法或屬性聲明為抽象的。這樣的方法並不具有聲明它們的類中提供的實現,盡管抽象類也可以包含非抽象方法,也就是說,已經為其方法提供了實現。抽象類不能直接實例化,而只能作為派生類。這樣的派生類必須為所有的抽象方法和屬性提供實現(使用 override 關鍵字),除非派生成員本身被聲明為抽象的。
下面的示例聲明了一個抽象的 Employee 類。我們還創建了一個名為 Manager 的派生類,它提供了定義在 Employee 類中的抽象方法 show() 的實現:
using System;
public abstract class Employee
{
// abstract show method
public abstract void show();
}
// Manager class extends Employee
public class Manager: Employee
{
string name;
public Manager(string name)
{
this.name = name;
}
//override the show method
public override void show()
{
Console.WriteLine("Name : " + name);
}
}
public class CreateManager
{
public static void Main(string[] args)
{
// Create instance of Manager and assign it to an Employee reference
Employee temp = new Manager("John Chapman");
// Call show method. This will call the show method of the Manager class
temp.show();
}
}
這段代碼調用了由 Manager 類提供的 show() 實現,並且在屏幕上打印出雇員的名字。
接口
接口是一種“主干類”,包含方法簽名但是沒有方法的實現。在這個方面,接口與抽象類一樣,只包含抽象方法。C# 接口非常類似於 Java 接口,工作原理基本一樣。
接口的所有成員都定義為公共成員,並且接口不能包含常量、字段(私有數據成員)、構造函數、析構函數或任何類型的靜態成員。如果為接口的成員指定任何修飾符,編譯器將會產生錯誤。
為了實現接口,我們可以從接口派生類。這樣的派生類必須為所有接口的方法提供實現,除非派生類聲明為抽象的。
接口的聲明與 Java 完全一樣。在接口定義中,通過單獨使用 get 和 set 關鍵字,屬性僅指示它的類型,以及它是只讀的、只寫的還是可讀寫的。下面的接口聲明了一個只讀屬性:
public interface IMethodInterface
{
// method signatures
void MethodA();
int MethodB(float parameter1, bool parameter2);
// properties
int ReadOnlyProperty
{
get;
}
}
用一個冒號來代替 Java 的實現關鍵字,類就可以繼承此接口。實現類必須提供所有方法的定義以及任何必需的屬性訪問器:
public class InterfaceImplementation : IMethodInterface
{
// fields
private int count = 0;
private int ID;
// implement methods defined in interface
public void MethodA()
{
...
}
public int MethodB(float parameter1, bool parameter2)
{
...
return integerVariable;
}
public int ReadOnlyProperty
{
get
{
return count;
}
}
// add extra methods if required
}
實現多個接口
通過使用下面的語法,一個類可以實現多個接口:
public class MyClass : interfacename1, interfacename2, interfacename3
如果一個類實現多個接口,則成員的名稱會存在二義性,通過使用屬性或方法名的完全限定符可以解決這個問題。換句話說,通過使用方法的完全限定名來指示它屬於哪個接口(例如屬於 IMethodInterface.MethodA),派生類可以解決這種沖突。
運算符重載
與 C++ 一樣,C# 允許我們重載運算符,以供在我們自己的類中使用。這可能使得用戶定義的數據類型看起來很自然,並且可以在邏輯上作為基本數據類型使用。例如,我們可以創建一個新的名為 Complex 的數據類型來表示一個復雜的數字,並且提供一些方法,以使用標准的算術運算符對這樣的數字進行算術運算,例如使用 + 運算符來使兩個復雜的數字相加。
為了重載一個運算符,我們編寫了一個函數,它將需要重載的運算符的符號放在名稱 operator 的後面。例如,我們可以這樣來重載 + 運算符:
public static complex operator+(complex lhs, complex rhs)
所有的運算符重載都是類的靜態方法。同時也需要注意,如果您重載等於運算符 (==),您還必須重載不等於運算符 (!=)。
可以重載的運算符的完整列表如下:
• 一元運算符: +, -, !, ~, ++, --, true, false • 二元運算符: +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=下一個示例創建了一個 Complex 類,該類重載 + 和 - 運算符:
using System;
public class complex
{
private float real;
private float img;
public complex(float p, float q)
{
real = p;
img = q;
}
public complex()
{
real = 0;
img = 0;
}
public void Print()
{
Console.WriteLine("{0} + {1}i", real, img);
}
// Overloading '+' operator
public static complex operator+(complex lhs, complex rhs)
{
complex sum = new complex();
sum.real = lhs.real + rhs.real;
sum.img = lhs.img + rhs.img;
return (sum);
}
// Overloading '-' operator
public static complex operator-(complex lhs, complex rhs)
{
complex result = new complex();
result.real = lhs.real - rhs.real;
result.img = lhs.img - rhs.img;
return (result);
}
}
此類允許我們使用代碼來創建和操作兩個復雜的數字,如下所示:
using System;
public class ComplexClass
{
public static void Main(string[] args)
{
// Set up complex numbers
complex A = new complex(10.5f,12.5f);
complex B = new complex(8.0f,4.5f);
complex C;
// Print object A and B
Console.Write("Complex Number A: ");
A.Print();
Console.Write("Complex Number B: ");
B.Print();
// Add A and B, print result
C = A + B;
Console.Write("\nA + B = ");
C.Print();
// Subtract A and B, print result
C = A - B;
Console.Write("A - B = ");
C.Print();
}
}
正如程序所演示的,我們現在可以很直觀地對屬於復雜類的對象使用加減運算符。下面是我們得到的輸出:
Complex Number A: 10.5 + 12.5i
Complex Number B: 8 + 4.5i
A + B = 18.5 + 17i
A - B = 2.5 + 8i
雖然 Java 在內部為字符串連接重載了 + 運算符,但是它並不支持運算符重載。
異常
C# 中的異常處理與 Java 非常相似。
在程序執行的過程中,無論什麼時候出現了嚴重錯誤,.NET 運行庫都會創建一個 Exception 對象來處理該錯誤。在 .NET 中,Exception 是所有異常類的基類。從 Exception 基類派生了兩種類別的異常:System.SystemException 和 System.ApplicationException。System 命名空間中的所有類型都是從 System.SystemException 派生的,而用戶定義的異常應該從 System.ApplicationException 派生,以便區分運行庫錯誤和應用程序錯誤。一些常見的 System 異常包括:
• IndexOutOfRangeException — 使用了大於數組或集合大小的索引 • NullReferenceException — 在將引用設置為有效的實例之前使用了引用的屬性或方法 • ArithmeticException — 在操作產生溢出或下溢時引發的異常 • FormatException — 參數或操作數的格式不正確
與 Java 中一樣,當我們有容易引起異常的代碼時,我們應該將此代碼放在 try 塊中。緊接其後的是一個或多個提供錯誤處理的 catch 塊,並且我們還可以對任何我們想執行但又不知道是否引發異常的代碼使用 finally 塊。
注意:當使用多個 catch 塊時,捕獲異常的代碼必須以升序的順序放置,這樣就只有第一個與引發的異常相匹配的 catch 塊會被執行。C# 編譯器會強制這樣做,而 Java 編譯器不會這樣做。
C# 也與 Java 一樣,catch 塊並不需要參數;在缺少參數的情況下,catch 塊適用於任何 Exception 類。
例如,當從文件中進行讀取時,可能會遇到 FileNotFoundException 或 IOException,首先,我們需要放置更具體的 FileNotFoundException 處理程序:
try
{
// Code to open and read a file
}
catch (FileNotFoundException fe)
{
// Handle file not found exception first
}
catch (IOException ioe)
{
// Now handle any other IO exceptions
}
catch
{
// This block will catch all other exceptions
}
finally
{
// Executed whether or not an exception occurs, often to release resources
}
通過從 Exception 派生,我們可以創建我們自己的異常類。例如,下面的代碼創建了一個 InvalidDepartmentException 類,比方說,當某個部門的一個新雇員記錄為無效時,我們可能引發該類。用戶定義的異常的類構造函數使用 base 關鍵字來調用基類構造函數,並發送一個適當的消息:
public class InvalidDepartmentException : System.Exception
{
public InvalidDepartmentException(string Department) : base(
"Invalid Department: " + Department){ }
}
那麼,我們可以用下面的代碼引發這種類型的異常:
if (!(Department == "Sales" | Department == "Marketing"))
{
throw new InvalidDepartmentException(Department);
}
注意,C# 不支持 checked 異常。在 Java 中,這些是使用 throws 關鍵字進行聲明的,以便指定一個方法可能引發一個特殊類型的異常,此異常必須由調用代碼進行處理。
高級 C# 技術索引器
索引器提供了一種以與數組相同的方式訪問類或結構的方法。例如,我們可能有表示我們公司內某個部門的類。這個類可以包含該部門中所有雇員的名字,而索引器可以允許我們訪問這些名字,如下所示:
myDepartment[0] = "Fred";
myDepartment[1] = "Barney";
等等。在類的定義中,通過定義具有如下簽名的屬性,可以啟用索引器:
public type this [int index]
然後,我們提供 get 和 set 方法作為標准屬性,而當使用索引器時,這些訪問器指定哪些內部成員被引用。
在下面的簡單示例中,我們創建了一個名為 Department 的類,此類使用索引器來訪問該部門的雇員(在內部表示為一個字符串數組):
using System;
public class Department
{
private string name;
private const int MAX_EMPLOYEES = 10;
private string [] employees = new string [MAX_EMPLOYEES];
public Department(string deptName)
{
name = deptName;
}
public string this [int index]
{
get
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
return employees[index];
}
else
{
throw new IndexOutOfRangeException();
//return "Error";
}
}
set
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
employees[index] = value;
}
else
{
throw new IndexOutOfRangeException();
//return "Error";
}
}
}
// Other methods and properties as usual
}
然後,我們可以創建這個類的一個實例,並且對其進行訪問,如下所示:
using System;
public class SalesDept
{
public static void Main(string[] args)
{
Department sales = new Department("Sales");
sales[0] = "Nikki";
sales[1] = "Becky";
Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
}
}
屬性
為了增加有關類型的聲明性信息,C# 引入一種叫做屬性的新機制。有關類型的其他信息放在類型定義前面的聲明性標記中。下面的示例向您展示了如何利用 .NET 框架屬性來修飾一個類或方法。
在下面的示例中,通過添加 WebMethod 屬性,GetTime 方法被標記為一個 XML Web 服務。
using System;
using System.Web.Services;
public class Utilities : WebService
{
[WebMethod]
public string GetTime()
{
return DateTime.Now.ToShortTimeString();
}
}
通過添加 CODE>WebMethod 屬性,.NET 框架現在會自動處理調用此函數所必需的 XML/SOAP 交換。可以調用這個 Web 服務來檢索下列值:
<?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://tempuri.org/">7:26 PM</string>
在下面的示例中,通過添加 Serializable() 屬性,Employee 類被標記為 Serializable。雖然 salary 字段被標記為 public,但是它不會被序列化,因為它是用 NonSerialized() 屬性標記的。
using System;
[Serializable()]
public class Employee
{
public int ID;
public string Name;
[NonSerialized()] public int Salary;
}
有關創建自定義屬性的信息,請參見 。
委托 (Delegate)
C++、Pascal 和其他一些支持函數指針概念的語言都允許我們在運行時選擇我們希望調用的函數。
Java 並沒有提供任何具有函數指針功能的結構,但是 C# 卻通過 System.Delegate 類提供了此功能。一個委托實例封裝一個方法,它是一個可調用實體。
對於實例方法,委托由包含類的實例以及該實例中的方法組成。對於靜態方法,可調用實體由一個類和此類中的靜態方法組成。因此,委托可用於調用任意對象的函數,並且委托是面向對象、類型安全且可靠的。
在定義和使用委托時有三個步驟:
• 聲明 • 實例化 • 調用我們可以用下面的語法聲明一個委托:
delegate void myDelegate();
然後,就可以用這個委托來引用返回 void 而且不帶任何參數的所有函數。
類似地,要為帶有 string 參數並返回 long 的任何函數創建一個委托,我們應該使用下面的語法:
delegate long myDelegate(string mystring);
然後,我們就可以將該委托指派給具有此簽名的任何方法,如下所示:
myDelegate operation = new myDelegate(methodName);
重新指派委托
委托對象是不可改變的,也就是說,與它們匹配的簽名一旦設置就不能改變。然而,我們可以指向另一個方法,只要它們都具有相同的簽名即可。例如:
delegate myDelegate(int a, int b)
myDelegate operation = new myDelegate(Add);
operation = new myDelegate(Multiply);
這裡,我們將 operation 重新指派給一個新的委托對象,這樣 operation 就可以調用 Multiply 方法。只有 Add() 和 Multiply() 都具有相同的簽名時,我們才可以這樣做。
調用委托
調用委托相當簡單,只需要將委托變量的名稱替換成方法的名稱即可:
delegate long myDelegate(int i, int j);
myDelegate operation = new myDelegate(Add);
long lresult = operation(10, 20);
這裡用值 10 和 20 調用 Add 方法,返回一個長整型結果,並將其賦給變量 lresult。
讓我們創建一個程序來快速地演示委托的創建、實例化和調用:
using System;
public class DelegateClass
{
delegate long myDelegate (int i, int j);
public static void Main(string[] args)
{
myDelegate operation = new myDelegate(MathClass.Add);
Console.WriteLine("Call to Add method through delegate");
long l = operation(10, 20);
Console.WriteLine("Sum of 10 and 20 is " + l);
Console.WriteLine("Call to Multiply method thru delegate");
operation = new myDelegate(MathClass.Multiply);
l = operation(1639, 1525);
Console.WriteLine("1639 multiplied by 1525 equals " + l);
}
}
public class MathClass
{
public static long Add (int i, int j)
{
return (i+j);
}
public static long Multiply (int i, int j)
{
return (i*j);
}
}
我們會得到這樣的輸出:
Call to Add method through delegate
Sum of 10 and 20 is 30
Call to Multiply method through delegate
1639 multiplied by 1525 equals 2499475
如前所述,委托實例必須包含對象引用。在上面的示例中,通過將方法聲明為靜態的(意味著我們自己不需要指定對象引用),我們避免了這樣做。然而,如果委托引用一個實例方法,就必須給出對象引用,如下所示:
MathClass obj = new MathClass();
myDelegate operation = new myDelegate(obj.Power);
其中,Power 是 MathClass 方法的一個實例。因此,如果 MathClass 的方法沒有聲明為靜態的,我們就可以通過委托來調用它們,如下所示:
using System;
public class DelegateClass
{
delegate long myDelegate(int i, int j);
public static void Main(string[] args)
{
MathClass mathObj = new MathClass();
myDelegate operation = new myDelegate(mathObj.Add);
Console.WriteLine("Call to Add method through delegate");
long l = operation(10, 20);
Console.WriteLine("Sum of 10 and 20 is " + l);
Console.WriteLine("Call to Multiply method thru delegate");
operation = new myDelegate(mathObj.Multiply);
l = operation(1639, 1525);
Console.WriteLine("1639 multiplied by 1525 equals " + l);
}
}
當這些方法聲明為 static 時,如果您運行此程序,您就會得到同前面一樣的輸出。
委托和事件
.NET 框架也將委托廣泛應用於事件處理任務(像 Windows 或 Web 應用程序中的按鈕單擊事件)。雖然在 Java 中事件處理通常通過實現自定義偵聽器類來完成,但是 C# 開發人員可以利用委托來進行事件處理。事件被聲明為帶有委托類型的字段,只是在事件聲明前面加上 event 關鍵字。通常,事件被聲明為公共的,但是任何可訪問性修飾符都是允許的。下面的代碼顯示了委托和事件的聲明。
public delegate void CustomEventHandler(object sender, EventArgs e);
public event CustomEventHandler CustomEvent;
事件委托是多路廣播的,這意味著它們可以具有對多個事件處理方法的引用。通過維護事件的注冊事件處理程序列表,委托可以擔當引發事件的類的事件發送程序。下面的示例向您展示了可以如何給多個函數預訂事件。類 EventClass 包含委托、事件和調用事件的方法。注意,只能從聲明事件的類中調用事件。然後,類 TestClass 就可以使用 +=/-= 運算符來預訂/取消預訂事件。當調用 InvokeEvent() 方法時,它會激發此事件,而已經預訂此事件的任何函數都會同步激發,如下面的代碼所示:
using System;
class TestClass
{
static void Main(string[] args)
{
EventClass myEventClass = new EventClass();
// Associate the handler with the events
myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent1);
myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent2);
myEventClass.InvokeEvent();
myEventClass.CustomEvent -= new EventClass.CustomEventHandler(CustomEvent2);
myEventClass.InvokeEvent();
}
private static void CustomEvent1(object sender, EventArgs e)
{
Console.WriteLine("Fire Event 1");
}
private static void CustomEvent2(object sender, EventArgs e)
{
Console.WriteLine("Fire Event 2");
}
}
public class EventClass
{
public delegate void CustomEventHandler(object sender, EventArgs e);
//Declare the event using the delegate datatype
public event CustomEventHandler CustomEvent;
public void InvokeEvent()
{
CustomEvent(this, EventArgs.Empty);
}
}
此程序的輸出如下:
Fire Event 1
Fire Event 2
Fire Event 1
垃圾回收
在 C 和 C++ 中,許多對象一旦聲明,就需要編程人員在對象可以安全使用之前給它們分配資源。在對象使用完資源之後,將這些資源釋放回自由內存池也是編程人員的責任。如果資源沒有釋放,當越來越多的資源被不必要地消耗時,就可以說代碼洩漏內存。而在另一方面,如果資源過早地釋放,則可能會發生數據丟失、其他內存區域破壞和 null 指針異常。
為了防止這些危險的發生,Java 和 C# 都通過一個應用程序來獨立地管理所有對象的生命周期。
在 Java 中,JVM 通過跟蹤所分配資源的引用來處理未使用的內存的釋放。只要 JVM 檢測到一個資源不再被一個有效的引用加以引用,該資源就會被當作垃圾回收。
在 C# 中,通過具有與 JVM 類似功能的公共語言運行庫 (CLR) 來處理垃圾回收。CLR 垃圾回收器周期性檢查內存堆,以查看是否有未引用的對象,並釋放這些對象所占用的資源。
安全代碼和不安全代碼
C# 的一個特別值得注意的特性是它支持非類型安全代碼。通常,CLR 負責監視 IL(Intermediate Language,中間語言)代碼的行為,並阻止任何有問題的操作。然而,有時我們希望直接訪問低級功能(例如 Win32 API 調用),只要我們負責保證這樣的代碼操作正確,我們就會被允許這樣做。這樣的代碼必須放在源代碼的不安全塊中。
unsafe 關鍵字
進行低級 API 調用、使用指針算法、執行一些其他不安全操作的 C# 代碼必須放在用 unsafe 關鍵字標記的塊中。下面任何一種情況都可以標記為 unsafe:
•
一個完整的方法
•
一段放在括號中的代碼塊
•
一個單獨的語句
下面的示例演示了上面所有三種情況中 unsafe 的使用:
using System;
class UnsafeClass
{
unsafe static void PointyMethod()
{
int i=10;
int *p = &i;
Console.WriteLine("*p = " + *p);
string address = "Pointer p = " + int.Format((int) p, "X");
Console.WriteLine(address);
}
static void StillPointy()
{
int i=10;
unsafe
{
int *p = &i;
Console.WriteLine("*p = " + *p);
string address = "Pointer p = " + int.Format((int) p, "X");
Console.WriteLine(address);
}
}
static void Main()
{
PointyMethod();
StillPointy();
}
}
在這段代碼中,整個 PointyMethod() 方法被標記為 unsafe,因為該方法聲明並使用了指針。與這段代碼一樣,一旦某個代碼塊再次使用指針,StillPointy() 方法就將其標記為 unsafe。
有關不安全代碼的更多信息,請參見 。
fixed 關鍵字
在安全代碼中,垃圾回收器在其生命周期內可以相當自由地移動一個對象,它的任務是組織和壓縮可用資源。然而,如果我們的代碼使用指針,這種行為就可能很容易導致意外的結果,因此,我們可以使用 fixed 關鍵字來指示垃圾回收器不要移動特定的對象。
下面的代碼展示了如何使用 fixed 關鍵字來確保在 PointyMethod() 方法中的代碼塊執行期間系統不會移動數組。注意,fixed 僅僅使用在不安全代碼中:
public class FixedClass
{
public static void PointyMethod(char[] array)
{
unsafe
{
fixed (char *p = array)
{
for (int i=0; i<array.Length; i++)
{
Console.Write(*(p+i));
}
}
}
}
static void Main ()
{
char[] array = { 'H', 'e', 'l', 'l', 'o' };
PointyMethod(array);
}
}
小結
雖然 Microsoft 和其他廠商都已經為 .NET 平台引入了許多語言,但是 C# 是非常類似 Java 的一種語言,並且它非常適合希望從 J2EE 遷移到 .NET 平台的開發人員。
本文比較和對比了這兩種語言。在許多方面,C# 具有 C++ 的強大功能、Java 的簡潔優美,以及 Visual Basic 開發的簡易性,我希望本文已經說明了這一點。