一、簡介
面向對象(OO)編程在應用設計中已經發展二十來年了。程序不再是一系列函數的堆徹(象一些范例那樣的程序),而是對象的集合,每個對象都有其獨特的屬性和方法來與其它對象打交道。
"C"語言系列是面向對象設計發展的最好例子。C++為開發者提供了優秀的面向對象編程工具,程序員可以顯式地創建構造函數,拷貝構造函數,重載操作符,使用模板等等。
象C++這樣復雜語言的主要問題是程序員要花上好幾個月來掌握面向對象設計本質。新程序員必須學會掌握模板,函數重載,當然還要會創建和使用功能良好的類。
微軟公司給C#(讀為C-Sharp)賦予C++某些面向對象的本質,比如模板,但改變了類的創建方法。本文,我將對比C++和C#的類,並著重說明微軟在C#中類創建和使用方面的改變。
本文對象是熟練的C++程序員,通過一些細節來解釋C#面向對象的本質。
二、C#的類有了哪些改變?
如你所知,C#是部分基於C++,部分基於Java語法的語言。C#中還有一些細節上的改變,使得它可以用於現代設計。當你開始用C#建類時就會立即看到這點。讓我們通過一個簡單的例子開始了解在C++和C#中是如何建類並進行實例化的。
C++版本:
#include
class MyClass
{
public: void doSomething()
{ std::cout << "This is some text";
}
};
void main()
{ MyClass mc;
mc.doSomething();
}
C# 版本:
using System;
class MyClass
{
public void doSomething()
{
Console.WriteLine("This is some text");
}
}
class EntryPoint
{ public static void Main()
{ MyClass mc = new MyClass();
mc.doSomething();
}
}
上面的代碼中有幾個不同之處。
首先,C++用#include包含語句來指明包含文件iostream.h的物理路徑。C#則告訴編譯器程序將在System命名空間下操作,所有的命名空間和類都屬於System命名空間。C#通過命名空間的名字來決定程序的作用范圍(本例中只有System一個命名空間),而不用指明物理路徑的包含文件方法。
其次,C#的主程序用Main(注意M是大寫)。
第三,C++的類聲明結束後要在最後的大括號後面用分號結尾。C#則可用可不用,往往都是省略。
第四,你能看到在C#中必須顯式地聲明方法和成員的作用域。若不加聲明,缺省為私有(只有類成員可以訪問),這點與C++一樣。C#中有5種作用域:
公有(public):其他類成員也可以訪問
私有(private):只有類成員才能訪問
保護(protected):類成員和繼承類成員可以訪問
內部(internal):只有匯編裡的成員才能訪問(C#的匯編是代碼和資源數據的結合,以asmx作文件後綴)
內部保護(protected internal):類成員和繼承類成員可以訪問
最後,與Java一樣,C#的方法也可以聲明為靜態(static)的。靜態變量的使用在C#和C++是一樣的。在C#裡,可以這樣創建並調用類的靜態方法:
using System;
class MyClass
{ public static void doSomething()
{Console.WriteLine("This is some text");
}
};
class EntryPoint
{
public static void Main()
{
MyClass.doSomething();
} }
注意,這裡直接使用類聲明而沒有創建類的實例。這是為C#語言增加的非常便利的使用方法,可以節省我們的時間和內存。就是說,不要創建類實例,可以直接調用類的方法。
三、用類修飾語限制對類的訪問
以前只能對類成員和類方法設定限制,但不能對類實體作限制。C#可以通過聲明類修飾語來對類的實例實行限制,如上節提到的作用域。
C++不能對整個類作限制。看一下C++的類聲明:
class Car
{
public:
Car();
Car(Car &c);
virtual ~Car();
private:
int numCars;
Car* previous;
Car* next;
};
這裡有二種訪問類型:公有(public)和私有(private)。繼承或將類Car實例化後,程序只能繼承這些代碼,不能作其它變動,如果要作其它變動就不能將其作為基類。
C#對此了改變。可以附加訪問修飾語來限制類成員和方法以及類實例的訪問權。C#設定8個訪問權限:
公有(public):可以被所有其它的類訪問。沒有其它限制修飾語,它的公有性質就一直是缺省的。
私有(private):只有類成員才能訪問。
保護(protected):類成員和繼承類成員可以訪問。
內部(internal):只有匯編裡的成員才能訪問(C#的匯編是代碼和資源數據的結合,以asmx作文件後綴)。
內部保護(protected internal):類成員和繼承類成員可以訪問。
密封(sealed):所有繼承類都不能訪問。無論直接或間接地將它作為基類,C#編譯器都會跳錯。
抽象(abstract):與C++的虛(virtual)類或虛方法相似,抽象類不能直接實例化,抽象函數含有函數名。但在作為基類或繼承類時可以使用。
新建(new):用new創建嵌套類,可以隱藏繼承方式,告訴編譯器創建一個類的新版本。
舉例來說,如果要創建一個類,並且這個類不能被作為基類或繼承,那麼就要創建一個密封類:
sealed class Car
{
public void paintCar()
{
// Code to paint car goes here
}
}
這時如果要創建一個繼承類RedCar:
internal class RedCar : Car
{
// Won't work.
}
C#編譯器就會跳錯:
error CS0509: 'RedCar' : cannot inherit from sealed class 'Car' (不能從密封類'Car'繼承)。
四、C++和C#中的虛函數
C++和C#都支持虛函數。在下面的C++例子裡,有二個類,分別稱為Base(基類)和Derived(繼承類):
#include
using namespace std;
class Base
{
public:
void doWork()
{
cout << "Base class working";
}
protected:
virtual void doWork1() = 0;
};
class Derived : public Base
{
public:
void doWork2()
{
cout << "Derived class working";
}
void doWork1()
{
cout << "Dervied pure virtual function working";
}
};
void main()
{
Derived d;
d.doWork1();
}
基類裡有二個函數,一個是doWork,另一個是純虛函數doWork1。doWork1只能被基類的繼承類使用。在繼承類(公有地繼承於基類)裡有一個新函數doWork2,和繼承於基類純虛函數的超越函數doWork1。
在C#裡實現同樣的功能要更容易些。看下面的C#代碼:
using System;
abstract class Base
{ public void doWork()
{ Console.WriteLine("Base class working");
}
public abstract void doWork1();
}
class Derived : Base
{ public void doWork2()
{ Console.WriteLine("Derived class working");
}
public override void doWork1()
{ Console.WriteLine("Dervied pure virtual function working");
}
};
class EntryPoint
{
public static void Main()
{
Derived d = new Derived();
d.doWork1();
}
}
C#將基類定義為抽象類,將doWork1定義為抽象方法。這樣就可以獲得與上述C++純虛函數同樣的功能。Base類只能作為基類或被含有doWork1超越函數的繼承類使用。
繼承類在超越抽象函數doWork1時,在函數前面加上超越前綴(override)。C#編譯器在發現繼承類裡的override關鍵字後,就檢查基類的同名函數。只要不是直接顯式地調用基類函數,編譯器總是使用繼承類中的方法。
為了讓繼承類直接操作基類成員和方法,C# 為基類命名了一個別名base。用這個別名,繼承類可以直接引用基類成員和方法。示例如下:
using System;
class first {
public void writeIt()
{ Console.WriteLine("Writing from base class");
}
}
class second : first
{
public second()
{
base.writeIt();
}
}
class EntryPoint
{
public static void Main()
{ second s = new second();
}
}
在上述例子中,有二個類。一個是基類(first),另一個是繼承類(second)。當創建second類的實例時,它的構造函數自動調用基類的writeIt方法,用控制台指令Console.WriteLine打印屏幕。由此引出C++和C#中的多態性。
五、C++和C#中的多態性實體的多態性使其具有多種表現形式。在C++和C#中處理多態性是很相像的。看一個簡單例子:
C++ 版本:
#include
#include
using namespace std;
class Person
{
public:
Person()
{
classType = "Person";
}
friend void ShowType(Person& p);
private:
string classType;
};
class Manager : public Person
{ public:
Manager()
{classType = "Manager";
}
friend void ShowType(Person& p);
private:
string classType;
};
void ShowType(Person& p)
{ cout << p.classType << endl;
} void main()
{ Person p;
Manager m;
ShowType(p);
ShowType(m); }
C# 版本:
using System;
class Person {
public Person()
{ classType = "Person";
}
public string classType;
}
class Manager : Person
{ public Manager()
{
classType = "Manager";
}
public new string classType;
}
class EntryPoint
{ public static void ShowType(ref Person p)
{ Console.WriteLine(p.classType);
}
public static void Main()
{
Person p = new Person();
Person m = new Manager();
ShowType(ref p);
ShowType(ref m);
}
}
在上面的例子裡,有一個基類Person,一個繼承於基類的Manager類。在EntryPoint類裡,創建了一個靜態函數ShowType,其表達為:
public static void ShowType(ref Person p)
注意參數裡的ref關鍵字。ref告訴C#編譯器向ShowType函數傳遞一個參數的引用(reference)。在C#中,如果不用ref關鍵字,函數的參數傳遞缺省為值(value)傳遞,將拷貝一個參數值傳遞到函數中去。
在C++中的參數引用傳遞表達為: void ShowType(Person& p)
C++用"&"符號表示參數引用使得程序員新手感到困惑,尤其是那些從VB轉過來的人。
在C#的主函數(entry point)裡,創建了二個新的Person對象,p和m:
Person p = new Person();
Person m = new Manager();
值得一提是,關鍵字new在C#和C++中用法是不一樣的。在C#裡,new只創建一個對象實例。這個對象依然創建在管理堆裡,但不返回指向對象的內存地址的指針。在上面的C#例子中,創建了二個Person類對象。第二個對象,m,卻是Manager類的對象。它使用Manager的構造函數而不是用Person的構造函數。
將Person類對象和Manager類對象引用到ShowType函數,記住,Manager是Person的繼承類,但C#的多態性將其表達為一個Person類:
ShowType(ref p);
ShowType(ref m);
當作用到ShowType函數時,它只處理Person對象。C#告訴它說m是Person繼承類的對象,它就將m按Person類處理了。所以用p和m作參數調用ShowType函數後得到的輸出為:
Person
Person
[譯者注:這樣解釋多態性有點離譜。這段 C#代碼的真正含義在於诠釋靜態函數的作用,而不是什麼多態性。上面一段C++代碼則可以看成多態性用法的解釋。]
六、結論
在我熟悉C#之前,我用了4年VB和2年C++。我可以負責地說,C#是我用過的語言中最具活力和靈活性並使人愉快的語言,而且它是100%面向對象的。如果你是一個C++程序員,現在想轉向電子商務編程或干脆就是想換一種更現代的語言,那麼就是C#了。有這麼三種原因:
如果會使用C#,你就能創建任何應用:Windows應用,控制台應用,Web應用和Web服務等等。
所有的.NET平台使用的語言都編譯成為中間語言(IL),並能按照系統環境進行優化。
非常非常容易將C++轉換成C#。