程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 類(1)- 構造函數,構造函數

類(1)- 構造函數,構造函數

編輯:C#入門知識

類(1)- 構造函數,構造函數


構造函數的最基本的作用是為類型的一個新的實例中所有的字段和屬性分配初始值。所以,根據其功能,他不需要(也沒有意義)返回值。他的函數名必須和類名相同。

引用類型的構造函數

任何時候,只要創建類或結構的一個實例,就會調用它的構造函數。類或結構可能有多個接受不同參數的構造函數。構造函數使得程序員可設置默認值、限制實例化以及編寫靈活且便於閱讀的代碼。

如果沒有為對象提供構造函數,則默認情況下 C# 將創建一個沒有任何參數的構造函數,該構造函數將會調用其基類的無參數的構造函數。如果基類也沒有則繼續上溯,直到object的公共構造函數,他什麼都不做。(通常對於所有的值類型,上溯到system.valuetype下對應的類型例如int32等,然後為值類型賦0,對於所有的引用類型,賦null)

如果我們顯式的寫出了任何一個構造函數(不管是否有輸入參數),c#都不會再為我們創建那個沒有參數的構造函數了

構造函數不能被繼承。不能在構造函數前面加abstract, virtual, new, sealed,override。

構造函數是方法,所以也可以有存取修飾詞。一般來說,構造函數都是PUBLIC的。但如果我們設定其為PRIVATE(私有構造函數),那麼他就不能被外部訪問。所造成的效果就是該類不能被實例化。私有構造函數是一種特殊的實例構造函數。它通常用在只包含靜態成員的類中。如果類具有一個或多個私有構造函數而沒有公共構造函數,則其他類(除了嵌套類)無法創建該類的實例。

構造函數之間可以通過this調用默認構造函數(沒有參數的那個)。通常來說,如果這麼做(構建構造函數鏈),則默認構造函數只能有一個,他的參數沒有限制。但只能有一個的原因是所有構造函數的名字都必須相同,所以如果超過一個,this將不知道要調用哪個。

Base關鍵字

子類若不顯式的調用父類的構造函數時,編譯器會自動調用父類的默認(無參)構造函數。可以用Base關鍵字指明要調用父類的哪個構造函數。

練習:以下代碼輸出什麼?

public class MyBaseClass
    {
        public MyBaseClass()
        {
            Console.WriteLine("In MyBaseClass()");
        }
        public MyBaseClass(int i)
        {
            Console.WriteLine("In MyBaseClass(" + i + ")");
        }
    }
    public class MyDerivedClass : MyBaseClass
    {
        public MyDerivedClass()
        {
            Console.WriteLine("In MyDerivedClass()");
        }
        public MyDerivedClass(int i)
        {
            Console.WriteLine("In MyDerivedClass(" + i + ")");
        }
        //public MyDerivedClass(int i, int j)
        //{
        //    Console.WriteLine("In MyDerivedClass(int i,int j)");
        //}
        public MyDerivedClass(int i, int j) : base(i)
        {
            Console.WriteLine("In MyDerivedClass(" + i + ",int " + j+ "):base(" + i + ")");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //Event1
            MyDerivedClass myObj1 = new MyDerivedClass();
            Console.WriteLine();
            //Event2
            MyDerivedClass myObj2 = new MyDerivedClass(4);
            Console.WriteLine();
            //Event3
            MyDerivedClass myObj3 = new MyDerivedClass(4,8);
            Console.WriteLine();
            Console.ReadKey();
        }
    }

 

解答: 

Event1: 實例化myObj1,自動調用父類(再往上就是system.object,其構造函數什麼都不做,下同)的無參構造函數,和自己的無參構造函數。

Event2: 實例化myObj2,自動調用父類的無參構造函數,和自己的有參構造函數。

Event3: 實例化myObj3,根據base關鍵字,顯式的調用父類的有參構造函數,和自己的有參構造函數。

輸出:

In MyBaseClass()

In MyDerivedClass()

In MyBaseClass()

In MyDerivedClass(4)

In MyBaseClass(4)

In MyDerivedClass(4,int 8):base(4)

值類型的構造函數

值類型的構造函數可以存在,但系統永遠不會自動的調用。我們必須顯式的調用值類型的構造函數(例如結構體的)。另外,C#不允許定義值類型的無參數構造器

 

靜態構造函數

對於類型中的非靜態字段,類型的每一個對象都會維護字段的獨立副本。相對而言,對於靜態字段,所有該類型的實例共享一個值。如果要定義一個所有對象都可以分享的數據點,就可以考慮使用靜態成員。

假設類中有若干個靜態的字段,在構造函數中,我們初始化他們的值,這時,假設我們在外部方法中更改了靜態字段的值,然後實例化一個新的類,新的類中靜態字段的值會被重置為初始化的值,如果我們不希望如此而是要保持靜態成員的值不變,就要借助靜態構造函數。靜態構造函數只會執行一次(在第一個該類型的實例被創建的時候),之後,無論再創建多少該類型的實例,都不會再次運行,這保證了類型中的靜態成員的值不受影響。

靜態構造函數具有以下特點:

下例選自CLR via c#, 下面代碼輸出什麼?

class Program
    {
        static void Main(string[] args)
        {
            B b = new B();
            Console.ReadKey();
        }
    }

    class A
    {
        public A(string text)
        {
            Console.WriteLine(text);
        }     
    }

    class B
    {
        static A a1 = new A("a1");
        A a2 = new A("a2");

        static B()
        {
            a1 = new A("a3");
        }

        public B()
        {
            a2 = new A("a4");
        }
    }

答案:

a1

a3

a2

a4

解釋:a2和a4肯定在後面這個較好理解,因為靜態構造函數先於其他構造函數執行。當創建一個新的B的實例時,先執行的是類中和靜態有關的語句,然後便是靜態構造函數,所以a1先於a3打印出來。下面一個例子可以看得更清楚:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             B b = new B();
 6             Console.ReadKey();
 7         }
 8     }
 9 
10     class A
11     {
12         public A(string text)
13         {
14             Console.WriteLine(text);
15         }     
16     }
17 
18     class B
19     {
20         static A a1 = new A("a1");
21         static int aProperty = 1;
22         int bProperty = 2;
23         A a2 = new A("a2");
24 
25         static B()
26         {
27             a1 = new A("a3");
28         }
29 
30         public B()
31         {
32             a2 = new A("a4");
33         }
34     }

假如我們開啟調試模式的話,那麼代碼(按照行號)的運行順序將是從行19開始,然後20-13至15-21-(並不會運行非靜態的部分)25-26-27-13至15-28(靜態部分結束)-22-23-13至15-31-32-13至15-33-6-結束。

靜態構造函數只會執行類中和靜態有關的語句(先初始化類中和靜態有關的變量,再執行靜態函數語句)。靜態構造函數只會執行一次。靜態構造函數的執行契機為初始化該類型之前

下面代碼輸出什麼?

 1 public class A
 2     {
 3         public static readonly int x;
 4         static A()
 5         {
 6             x = B.y + 1;
 7         }
 8     }
 9 
10     class B
11     {
12         public static int y = A.x + 1;
13 
14         static void Main(string[] args)
15         {
16             Console.WriteLine("x:{0},y:{1}。", A.x, y);
17             Console.ReadLine();
18         }
19     }

此題非常詭異,乍一看好像無法運行,讓我們慢慢分析。首先程序從類B開始運行,然後程序發現,類B擁有一個靜態的成員y,於是初始化之,而又沒有靜態構造函數,於是先將其初始化為0。(可以將第12行拆成兩行看,第一行是public static int y,第二行是y = A.x + 1)。此時y=0,然後令y等於A.x+1,程序又不知道A是啥,於是進入A類,初始化A的靜態成員x為0,然後執行A的靜態構造函數,此時因為B.y為0,所以可以順利的將x設置為0+1=1。

此時程序離開A類,回到第12行,y=1+1=2,最後,打印出來結果,x=1,y=2。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved