C#中一些易混淆概念總結(二)--------構造函數,this關鍵字,部分類,枚舉

目錄:

【C#小知識】C#中一些易混淆概念總結

繼上篇對一些C#概念問題進行細節的剖析以後,收穫頗多。以前,讀書的時候,一句話一掠而過,但是現在再去重讀的時候,每句話發現都包含大量的信息。這一篇繼續總結自己的學習筆記,給大家深度的剖析一些概念性問題,有助於大家對C#的理解。

--------------------------------------------------分割線---------------------------------------------

一,構造函數

我們先創建一個類,如下面的代碼:

class Program
    {
        static void Main(string[] args)
        {
       
        }
    }
  //創建一個Person類
    class Person
    {
    }


然後生成代碼。

我們使用.NET Reflector反編譯該程序集。會發現該類一被編譯,CLR會自動的爲該類創建一個默認的構造函數。如下圖:

022201265475879.png

所以在創建該對象的時候,會默認的爲該類生成一個無參數的空方法體的構造函數。如果我們不顯式的寫明構造函數,CLR會爲我們調用默認的構造函數。

class Person
    {
        //聲明有實現的構造函數
        public Person()
        {
            Console.WriteLine("我是超人!");
        }
    }


再次反編譯該程序集,會發現添加的構造函數覆蓋了C#編譯器默認爲該類生成的構造函數,如下圖:

022210156412070.png

所以,當程序員手動添加了任意類型的構造函數,C#編譯器就不會爲該類添加默認的構造函數。

構造函數的特點:

①訪問修飾符一般是Public②沒有返回值,方法名與類名稱一致;


二,This關鍵字的作用

①this關鍵字代表當前對象,當前運行在內存中的那一個對象。我們添加如下的代碼:

private int nAge;
        public int NAge
        {
            get { return nAge; }
            set { nAge = value; }
        }
        //聲明有實現的構造函數
        public Person()
        {
            this.NAge = 100;
            Console.WriteLine("我是超人!");
        }


這時候我們反編譯該程序集,會看到如下結果:

022232057819481.png

可以看到this關鍵字代替的就是當前的Person對象。

②this關鍵字後面跟“:”符號,可以調用其它的構造函數

我們再添加如下的代碼:

#region 對象的構造函數
        //聲明有實現的構造函數
        public Person()
        {
            this.NAge = 100;
            Console.WriteLine("我是超人!");
        }
        public Person(int nAge)
        {
            Console.WriteLine("超人的年齡{0}", nAge);
        }
     //使用this關鍵字調用了第二個一個參數的構造函數
        public Person(int nAge, string strName)
            : this(1)
        {
            Console.WriteLine("我是叫{0}的超人,年齡{1}", strName, nAge);
        }
        #endregion


我們創建該對象看看是否調用成功。在Main函數中添加如下代碼:

Person p = new Person(10,"強子");

我們運行代碼,看到的打印結果如下:

022239107971538.png

由結果我們可以分析出,當含有兩個默認參數的對象創建的時候應該先調用了一個參數的構造函數對對象進行初始化,然後有調用了含有兩個參數的構造函數對對象進行初始化。

那麼到底是不是這個樣子呢?看下邊的調試過程:

022245163753351.gif

通過上面的調試過程我們會發現,當構造函數使用this關鍵字調用其它的構造函數時,首先調用的是該調用的構造函數,在調用被調用的構造函數,先執行被調用的構造函數,在執行直接調用的構造函數。

爲什麼要這個順序執行?因爲我們默認的傳值是10,我們需要打印的超人的年齡是“10”,如果先執行直接調用的構造函數,就會被被調用構造函數覆蓋。

三,部分類

在同一命名空間下可以使用partial關鍵字聲明相同名稱的類(同一命名空間下默認不允許出現相同的類名稱),叫做部分類或者夥伴類。

如下圖,當在同一命名空間下聲明相同名稱的類,編譯器報錯:

022255518289949.png

當我們使用Partial關鍵字時,可以順利編譯通過,如下圖:

022257594694254.png

分別添加如下的代碼:

partial class  Person
    {
        private string strAddress;
        public string StrAddress
        {
            get { return strAddress; }
            set { strAddress = value; }
        }
        private string strNumber;
        public string StrNumber
        {
            get { return strNumber; }
            set { strNumber = value; }
        }
        public void Run()
        {
        }
    }
                                                                                        
    partial class  Person
    {
        #region 對象屬性
        private int nAge;
        public int NAge
        {
            get { return nAge; }
            set { nAge = value; }
        }
        private string strName;
        public string StrName
        {
            get { return strName; }
            set { strName = value; }
        }
                                                                                            
        #endregion
        #region 對象的構造函數
        //聲明有實現的構造函數
        public Person()
        {
            this.NAge = 100;
            Console.WriteLine("我是超人!");
        }
        public Person(int nAge)
        {
            Console.WriteLine("超人的年齡{0}", nAge);
        }
        public Person(int nAge, string strName)
            : this(1)
        {
            Console.WriteLine("我是叫{0}的超人,年齡{1}", strName, nAge);
        }
        #endregion
        public void Sing()
        {
        }
    }


我們再次反編譯該程序集,會發現如下的結果:

022303433919284.png

我們會發現使用Partial關鍵字的兩個同名類,被編譯成了同一個類。

所以部分類的特點:

①必須在同一個命名空間下的使用Partial關鍵字的同名類

②部分類其實就是一個類,C#編譯器會把它們編譯成一個類

③在一個夥伴類中定義的變量可以在另一個夥伴類中訪問(因爲他們就是一個類)。


四,Const關鍵字和Readonly關鍵字的區別

1)const關鍵字

在Main函數中添加如下的代碼:

conststring strName = "強子";          

Console.WriteLine("我的名字叫{0}",strName);

編譯過後,我反編譯該程序集發現如下結果:

022315237665949.png

發現定義的常量並沒有出現在反編譯的代碼中,而且使用Const常量的地方被常量代替了。

2)readonly關鍵字

添加如下代碼:

class cat
    {
        readonly string reOnlyName = "強子";
        public cat()
        {
            Console.WriteLine(reOnlyName);
        }
    }


生成後反編譯該程序集發現,如下結果:

022327589538902.png

我們發現被readonly修飾的變量並沒有被賦值,這是什麼回事呢?我們點擊cat類的構造函數時,看到如下結果:

022330090166033.gif

我們發現被readonly修飾的變量是在被調用的時候賦值的。

那麼被readonly修飾的變量的是就是不可變的麼?當然不是,由反編譯的結果我們知道,readonly修飾的變量是在被調用的時候在構造函數中被賦值的,那麼我們可以在構造函數中修改readonly的默認值

添加如下代碼

class cat
    {
        readonly string reOnlyName = "強子";
        public cat()
        {
            this.reOnlyName = "子強";
            Console.WriteLine(reOnlyName);
        }
    }


在Main()函數中添加如下的代碼:

  cat ct = new cat();

運行結果如下:

022335354698833.png

說明我們成功在構造函數中修改了readonly變量的值。

readonly和const的區別:

const常量在聲明的時候就必須賦初始值,這樣聲明變量可以提高程序的運行效率。而readonly變量聲明時可以不賦初始值,但一定要早構造函數中賦初始值。

也就是說,const變量在編譯的時候就要確定常量的值,而readonly是在運行的時候確定該變量的值的。


五,解析枚舉

枚舉的級別和類的級別一樣,可以自定義數據類型,可以在枚舉名稱後使用“:”來指明枚舉類型。看如下代碼:

//定義一個方向的枚舉類型,枚舉成員使用","分割
    enum Direction:string
    {
        east,
        west,
        south,
        north
    }


編譯會報錯,錯誤信息如下:

022350400948831.png

由此我們可以知道枚舉的數據類型是值類型。

因爲枚舉是數據類型,所以可以直接聲明訪問,如下代碼:

class Program
    {
        static void Main(string[] args)
        {
            //枚舉是數據類型可以直接聲明
            Direction dr = Direction.east;
            Console.WriteLine(dr);
            Console.ReadKey();
        }
    }
    //定義一個方向的枚舉類型,枚舉成員使用","分割
    enum Direction
    {
        east,
        west,
        south,
        north
    }


也可以這樣訪問枚舉類型

class Program
    {
        static void Main(string[] args)
        {
            //枚舉是數據類型可以直接聲明
           // Direction dr = Direction.east;
            Person p=new Person();
            //直接調用枚舉變量
            p.dir = Direction.east;
            Console.WriteLine(p.dir);
            Console.ReadKey();
        }
    }
     class Person
    {
        private string strName;
         //直接聲明枚舉變量
        public Direction dir;
    }


每一個枚舉成員都對應了一個整型的數值,這個數值默認從0開始遞增,可以通過強制轉換獲取該枚舉所代表的值。可以通過如下的代碼訪問:

Direction dr = Direction.east;          

int i = (int)dr;

我們還可以手動爲每一個枚舉成員賦值,代表的是整型數值,賦值後該枚舉成員所代表的值就是所賦的值。如下代碼:

enum Direction
    {
        east=1,
        west=0,
        south=2,
        north=3
    }


將字符串轉換成枚舉

string strDir = "east";
           //將字符串轉換成枚舉類型
           Direction d1=(Direction)Enum.Parse(typeof(Direction),strDir);
           //轉換的時候忽略大小寫
           Direction d2 = (Direction)Enum.Parse(typeof(Direction), strDir,true);


--------------------------------分割線----------------------------------------

最後我們再來探究一個空指針異常的問題

首先我們先聲明一個Dog類:

class Dog
    {
        private int nAge;
        public int NAge
        {
            get { return nAge; }
            set { nAge = value; }
        }
        private string strName;
        public string StrName
        {
            get { return strName; }
            set { strName = value; }
        }
    }


在Main()函數中我們這樣調用

Dog d = null;            

d.StrName = "旺旺";

結果會報錯,如下圖

030028173122939.png

我們已經爲屬性,封裝字段了,但是爲什麼沒有辦法給字段賦值呢?我們就來探究一下這個問題。

當我們實例化Dog對象,即

Dog d = new Dog();

.NET Framwork做了什麼工作呢?如下圖:

030107255026694.png


那爲什麼會報錯呢,原因如下圖:

030115561271529.png



-----------------------------------------------分割線-----------------------------------------------------------------

這次分享到這裏就結束了。其實蠻享受寫這個過程的。因爲在初次的學的時候理解了,如果再寫成博客就又加深了印象,最後希望大家都能養成了良好的學習習慣。


如果您覺得不錯,點擊右下角贊一下吧!您的支持,是我寫作的動力!

畢業實習交流羣:221376964。你也可以關注我的新浪微博進行交流。

1.png


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章