【解答】
(1) 以過程為中心和對象為中心的比較
結構化編程方法是以過程為中心的,當面對一個問題時,該方法側重於問題解決過程的層次結構。面向對象的分析和設計方法側重於對象。對象具有特定的行為和屬性,行為和屬性決定了對象與其他對象的交互作用方式,以及對象本身的行為方式。
(2) 公開數據和隱藏數據的比較
結構化編程方法對數據和過程僅僅進行簡單的包裝,這些數據和過程是公開的,或者說程序中的其他代碼可以訪問這些數據和過程。面向對象的實現隱藏了特定的數據,並且只把對象的特定行為公開給用戶。實現這些特定行為的代碼對用戶來說是不可見的,用戶只能訪問這些公開的行為。
(3) 單一單元和標准單元的比較
結構化編程方法是基於單一代碼單元的。面向對象的編程方法允許對象是獨立的。
(4) 一次性使用和可重用的比較
根據不同的實現,結構化過程可能無法重用。而面向對象的方法,對象是一個模塊單元。具有完備的實體,因此可以具有高度的可重用性。
(5) 有序算法和無序算法的比較
結構化編程方法所開發的程序,其結構往往是線性的(或者說是自頂向下的)。而面向對象的應用程序是一種基於消息或者事件驅動的程序類型。每個對象都可以向其他對象發送消息。Windows操作系統就是這樣的程序。
2. 簡要回答下列問題。
1) 舉例說明new關鍵字可用於那些方面?
2) sealed關鍵字的作用是什麼?什麼情況下需要使用sealed關鍵字?
3) 哪些關鍵字可以用於版本控制?
【解答】
1) 在C#中,new關鍵字可用作運算符或修飾符。作為運算符用於在堆上創建對象和調用構造函數。作為修飾符用於隱藏基類成員的繼承成員。
2) 在類聲明中使用sealed修飾符可防止其它類繼承此類。在方法聲明中使用sealed修飾符可防止擴充類重寫此方法。
sealed修飾符主要用於防止非有意的派生,但是它還能促使某些運行時優化。具體說來,由於密封類永遠不會有任何派生類,所以對密封類的實例的虛擬函數成員的調用可以轉換為非虛擬調用來處理。
3) override關鍵字和new關鍵字均可用於版本控制。
在C#中,默認情況下方法不是虛擬的。若要使方法成為虛擬方法,必須在基類的方法聲明中使用virtual修飾符。然後,派生類可以使用override關鍵字重寫基類中的虛擬方法,或使用new關鍵字隱藏基類中的虛擬方法。如果override關鍵字和new關鍵字均未指定,編譯器將發出警告,並且派生類中的方法將隱藏基類中的方法。
3. 簡要回答抽象類和接口的主要區別。
【解答】
抽象類和接口的一個主要差別是:類可以實現多個接口,但僅能從一個抽象類或任何其它類型的類繼承。
4. 使用委托的優點是什麼?委托和事件有什麼區別和聯系?
【解答】
C#中的委托類似於C或C++中的函數指針。使用委托使程序員可以將方法引用封裝在委托對象內。然後可以將該委托對象傳遞給可調用所引用方法的代碼,而不必在編譯時知道將調用哪個方法。與C或C++中的函數指針不同,委托是面向對象,而且是類型安全的。
C#中的“事件”是當對象發生某些事情時,類向該類的客戶提供通知的一種方法。事件最常見的用途是用於圖形用戶界面;通常,表示界面中的控件的類具有一些事件,當用戶對控件進行某些操作(如單擊某個按鈕)時,將通知這些事件。
使用委托來聲明事件。委托對象封裝一個方法,以便可以匿名調用該方法。事件是類允許客戶為其提供方法(事件發生時應調用這些方法)的委托的一種方法。事件發生時,將調用其客戶提供給它的委托。
5. 編寫一個控制台應用程序,完成下列功能,並回答提出的問題。
1) 創建一個類A,在構造函數中輸出“A”,再創建一個類B,在構造函數中輸出“B”。
2) 從A繼承一個名為C的新類,並在C內創建一個成員B。不要為C創建構造函數。
3) 在Main方法中創建類C的一個對象,寫出運行程序後輸出的結果。
4) 如果在C中也創建一個構造函數輸出“C”,整個程序運行的結果又是什麼?
【解答】
using System;
public class A
{
public A()
{
Console.WriteLine("A");
}
}
public class B
{
public B()
{
Console.WriteLine("B");
}
}
public class C : A
{
B newb = new B();
}
class MainClass
{
public static void Main()
{
C newc = new C();
Console.ReadLine();
}
}
輸出結果:
B
A
如果在C中也創建一個構造函數輸出“C”,即添加:
public C()
{
Console.WriteLine("C");
}
則整個程序運行的結果為:
B
A
C
6. 編寫一個控制台應用程序,完成下列功能,並寫出運行程序後輸出的結果。
1) 創建一個類A,在A中編寫一個可以被重寫的帶int類型參數的方法MyMethod,
並在該方法中輸出傳遞的整型值加10後的結果。
2) 再創建一個類B,使其繼承自類A,然後重寫A中的MyMethod方法,將A中接
收的整型值加50,並輸出結果。
3) 在Main方法中分別創建類A和類B的對象,並分別調用MyMethod方法。
【解答】
using System;
public class A
{
public virtual void MyMethod(int num)
{
num += 10;
Console.WriteLine(num);
}
}
public class B : A
{
public override void MyMethod(int num)
{
num += 50;
Console.WriteLine(num);
}
}
class MainClass
{
public static void Main()
{
A newa = new A();
newa.MyMethod(2);
B newb = new B();
newb.MyMethod(2);
Console.ReadLine();
}
}
輸出結果:
12
52
7. 假設Node類的每一個節點包括有兩個字段:m_data(引用節點的數據)和m_next(引用鏈接列表中的下一項)。這兩個字段都是由構造函數方法設置的。該類有兩個功能,第一個功能是通過名為Data和Next的只讀屬性訪問m_data和m_next字段。第二個功能是對System.Object的ToString虛擬方法進行重寫。試分別用類和泛型兩種方法編寫程序實現上述功能。
【解答】
using System;
class Node
{
Object m_data;
Node m_next;
public Node(Object data, Node next)
{
m_data = data;
m_next = next;
}
// 訪問結點數據
public Object Data
{
get { return m_data; }
}
// 訪問下一個結點
public Node Next
{
get { return m_next; }
}
// 獲取結點數據描述
public override String ToString()
{
return m_data.ToString();
}
}
// 鏈表結點類的泛型定義
class Node
{
T m_data;
Node m_next;
public Node(T data, Node next)
{
m_data = data;
m_next = next;
}
// 訪問結點數據
public T Data
{
get { return m_data; }
set { m_data = value; }
}
// 訪問下一個結點
public Node Next
{
get { return m_next; }
set { m_next = value; }
}
// 獲取結點數據描述
public override String ToString()
{
return m_data.ToString();
}
}
// 使用結點類型或泛型結點類型
class LinkedList
{
static void Main(string[] args)
{
//// 創建整數鏈表
//Node head = new Node(5, null);
//head = new Node(10, head);
//head = new Node(15, head);
////遍歷鏈表求整數和
//Int32 sum = 0;
//for (Node current = head; current != null;
// current = current.Next)
//{
// sum += (Int32)current.Data;
//}
//// 輸出結果
//Console.WriteLine("Sum of nodes = {0}", sum);
// 用泛型創建整數鏈表
Node head = new Node(5, null);
head = new Node(10, head);
head = new Node(15, head);
// 遍歷求和
Int32 sum = 0;
for (Node current = head; current != null;
current = current.Next)
{
sum += current.Data;
}
// 輸出
Console.WriteLine("Sum of nodes = {0}", sum.ToString());
}
}