上篇談了CLR支持的兩種類型:
reference-type 和
value-type,這篇來詳細談談類(class)。
類(class) 是最基礎的C# 類型。類是一個數據結構,將狀態(字段)和操作(方法和其他函數成員)組合在一個單元中。類為動態創建的類實例(instance) 提供了定義,實例也稱為對象(object)。類支持繼承(inheritance) 和多態性(polymorphism),這是派生類(derived class) 可用來擴展和專用化基類(base class) 的機制。類的實例使用new運算符創建,該運算符為新的實例分配內存、調用構造函數初始化該實例,並返回對該實例的引用。當不再使用對象時,該對象占用的內存將自動收回。在C# 中,沒有必要也不可能顯式釋放分配給對象的內存。
類包可以包含一下成員:
(
靜態成員(static member),或者是
實例成員(instance member)。靜態成員屬於類,實例成員屬於該類的實例)
成員
說明
常量
與類關聯的常量值
字段
類的變量
方法
類可執行的計算和操作
屬性
與讀寫類的命名屬性相關聯的操作
索引器
與以數組方式索引類的實例相關聯的操作
事件
可由類生成的通知
運算符
類所支持的轉換和表達式運算符
構造函數
初始化類的實例或類本身所需的操作
析構函數
在永久丟棄類的實例之前執行的操作
類型
類所聲明的嵌套類型
看下面例子:
public sealed class SomeType
{ // 1
// 嵌套類
private class SomeNestedType { } // 2
// 常量,只讀字段,靜態字段
private const Int32 c_SomeConstant = 1; // 3
private readonly String m_SomeReadOnlyField = "2"; // 4
private static Int32 s_SomeReadWriteField = 3; // 5
// 類型構造器
static SomeType() { } // 6
// 實例構造器
public SomeType() { } // 7
public SomeType(Int32 x) { } // 8
// 靜態方法和類型方法
public static void Main() { } // 9
public String InstanceMethod() {
return null; } // 10
// 實例屬性
public Int32 SomeProp
{ // 11
get {
return 0; } // 12
set { } // 13
}
// 有參索引器
public Int32
this[String s]
{ // 14
get {
return 0; } // 15
set { } // 16
}
// 實例事件
public event EventHandler SomeEvent; // 17
}
類型成員的可訪問性
CLR術語
C#術語
含義
public
public
訪問不受限制
Family or Assembly
protected internal
訪問僅限於此程序或從此類派生的類
Assembly
internal
訪問僅限於此程序
Family and Assembly
不支持
Family
protected
訪問僅限於此類或從此類派生的類
Private
private
訪問僅限於此類
靜態類
static只能用於類,不能用於結構,因為CLR總是允許值類型實例化,無法阻止。
C#編譯器對靜態類型進行了如下的限制:
-
靜態類必須直接從System.Object派生,從其他任何基類派生都沒有任何意義
-
靜態類不能實現任何接口,這是因為只有使用了類的一個實例,才可以調用類的接口方法
-
靜態類只能定義靜態成員(字段、方法、屬性和事件),任何實例成員都將導致編譯錯誤
-
靜態類不能作為字段、方法參數或者局部變量使用,這些玩意都是實例的變量,編譯器會報錯
public static class MyStatic
{
/// <summary>
/// 靜態字段
/// </summary>
private static string staticString = "Hello bangq";
/// <summary>
/// 靜態屬性
/// </summary>
public static string StaticString
{
get { return staticString; }
set { staticString = value; }
}
/// <summary>
/// 靜態方法
/// </summary>
public static void SayHello()
{
Console.WriteLine(staticString);
}
}
staticvoidMain(string[] args)
{
MyStatic.SayHello();//Hello bangq
MyStatic.StaticString = "你好 BangQ ";
Console.WriteLine(MyStatic.StaticString);//你好 BangQ
Console.ReadKey();
}
可以這樣說,靜態類是MonoState模式的一種體現。提到MonoState就不得不提Singleton(單例)模式。
classSingleton
{
///<summary>
///靜態成員
///</summary>
private static Singleton _instance = null;
///<summary>
///靜態屬性
///</summary>
public static Singleton Instance {
get {
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
private Singleton() { }//私有的構造函數
public void SayHello()
{
Console.WriteLine("hello bangq"); www.2cto.com
}
}
單例模式可以保證系統中只有一個對象的實例,通常情況下可以通過 一個靜態成員、一個靜態屬性或方法、一個私有構造函數來實現。
為了保證系統中只有一個實例,所以在單例模式中將構造函數設置成私有的,這樣就不能New一個實例了,必須通過獲取實例的屬性或者方法。所以當出現這種代碼時:Singletonsingleton = new Singleton();編譯器會報以下錯誤:
ConsoleAppDemo.Singleton.Singleton()' is inaccessible due to its protection level
如果仔細的看了上面Singleton的代碼可以發現,上述代碼如果在多線程的情況下可能會創建多個實例,如:兩個線程都執行到了
if(_instance == null),那麼第一個線程創建了一個實例,第二個線程又創建了一個實例。關於多線程的問題,在以後會做總結,這裡只簡單說一下,加個鎖就ok了。調整後的代碼如下。
///<summary>
///鎖
///</summary>
private static readonly object _lock = new object();
///<summary>
///靜態屬性
///</summary>
public static Singleton Instance {
get {
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
這個裡面還有很多技巧和可以優化的地方,可以參見博客園兄弟的文章:http://www.cnblogs.com/BoyXiao/archive/2010/05/07/1729376.html
下面再來說下Monostate 模式
publicclassSayHello{privatestringhelloString ="hello wordh";publicSayHello(stringmessage){this.helloString =message;}publicstringHelloString{get {returnhelloString;}set{this.helloString =value;}}}publicclassMonoState{privatestaticSayHelloHello;publicMonoState(){Hello=newSayHello("Hello Word");}publicSayHelloSay{get {returnHello;}set{Hello=value;}}}
mian函數裡面的內容
monostate.Say.HelloString = "你好,BangQ";
Monostate是另一種獲得“系統某個類別對象都表現為一個對象”的實現方式,Singleton模式重在通過保證系統中只有唯一實例,而Monostate模式重在保證唯一狀態, 當然Singleton模式更多的是用來 保證唯一實例。Monostate模式更多關注的是行為,即行為上的一致;Singleton模式更多關注的是結構,強制結構上的單一,僅產生唯一實例.關於設計模式方面內容以後有機會再詳細講,再回到類的成員上來。
常量和字段
在C#中使用const定義常量,由於常量的值不會變化,所以常量總是被視為類型定義的一部分,常量總是被視為靜態成員而不是實例成員。常量的值必須是在編譯時確定的。代碼引用一個常量時,直接提取常量的值,嵌入到生成的IL代碼中,所以在運行時不需要為常量分配任何內存。不能獲取常量的地址,也不能以引用的方式傳遞常量。CLR via C# 裡面的例子:
usingSystem;
publicsealedclassSomeLibraryType {
public const Int32 MaxEntriesInList = 50;
}
classProgram
{
static void Main(string[] args)
{
Console.WriteLine(SomeLibraryType.MaxEntriesInList)
}
}
使用ILSpy可以看到 這行代碼IL_0XXX: ldc.i4.s 50 證明了上述所說,const直接嵌入到IL代碼裡面
【注意:常量不能定義為Static,因為常量總是隱式為static】
一個程序集A定義了一個常量,程序集B引用了這個常量,當程序集A中的常量發生更改時,只有B程序集重新編譯才能使新的常量生效。如果想要在運行時獲取一個常量,建議使用只讀的字段。
字段(field)是一種數據成員,其中容納了一個值類型的實例或者是一個引用類型的引用。下表總結了字段的修飾符。
CLR術語
C#術語
說明
Static
static
類狀態的一部分,而不是實例狀態的一部分
InitOnly
readonly
只能由構造器方法中的代碼寫入
Volatile
volatile
編譯器,CLR或硬件不會執行一些“線程不安全”的優化措施。
readonly也可以定義系統中恆定不變的量。和const不同的是,readonly指定初始值後可以構造函數中更改。readonly的值也可以通過別的手段去更改,比如反射。小結一下:const是編譯是常量,readonly是運行時常量。const比較高效,上面說了,無需分配內存。推薦使用static readonly 代替const。
本來想總結下類的其他成員,發現篇幅有點超了,下篇在寫吧
作者 BangQ