大話C#之屬性


前言

俗話說得好:工欲善其事,必先利其器。要想玩轉OOP設計出一個優秀的類型,屬性是必不可少的,那么我們今天就來說說c#中關於屬性的二三事。

屬性(property)分為無參屬性(parameterless property)和有參數性(parameterful property),在c#中有參屬性又被稱作索引器(indexer),以后看見索引器就別再感到陌生啦,本質上就一屬性嘛。廢話不多說,下面趕緊說說無參屬性和索引器。

無參屬性

話說在很久很久以前,那時候都還沒有屬性,但是設計類型時總會需要定義可以被獲取或者更改的狀態信息,當時人們是怎么干的呢?先來看看下面這段代碼:

public sealed class SchoolClass {
        /// <summary>
        /// 班級名稱
        /// </summary>
        public String Name;
        /// <summary>
        /// 學生人數
        /// </summary>
        public Int32 StudentNum;
    }
//創建一個SchoolClass類型
SchoolClass sc = new SchoolClass();
//設置班級名稱
sc.Name = "初一三班";
//設置學生人數
sc.StudentNum = 80;
//輸出“初一三班”
Console.WriteLine(sc.Name);

這段代碼中首先定義了一個類型,類型中定義了兩個字段,創建類型的實例以后,輕松地設置了類型中的狀態信息。不可否認,這種做法是可以達到讀取和設置對象狀態信息的目的,但是又引入了一個新的問題,類型使用者可以很輕松地將StudentNum設置為-10(稍微一想就明白這不符合正常的邏輯,怎么人數會比0小呢?)。也許有人會說了,這簡單啊把StudentNum的可訪問級別設為private就完了唄,這當然起到了一定作用,但是在類型外引用該類型怎么訪問StudentNum呢?既然討論不出結果,就讓我們看看前人是怎么解決的,請看下面這段代碼:

public sealed class SchoolClass
    {
        /// <summary>
        /// 班級名稱,現在字段是私有的
        /// </summary>
        private String Name;
        /// <summary>
        /// 學生人數,現在字段是私有的
        /// </summary>
        private Int32 StudentNum;
        /// <summary>
        /// 修改Name值的訪問
        /// </summary>
        /// <param name="value"></param>
        public void SetName(String value)
        {
            Name = value;
        }
        /// <summary>
        /// 獲取Name值的方法
        /// </summary>
        /// <returns></returns>
        public String GetName()
        {
            return Name;
        }
        /// <summary>
        /// 設置StudentNum的方法
        /// </summary>
        /// <param name="value"></param>
        public void SetNum(Int32 value)
        {
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("value", value.ToString(), "人數必須大於0!");
            }
            StudentNum = value;
        }
        /// <summary>
        /// 獲取StudentNum的方法
        /// </summary>
        /// <returns></returns>
        public Int32 GetNum()
        {
            return StudentNum;
        }
    }
//創建一個SchoolClass類型
SchoolClass sc = new SchoolClass();
//設置班級名稱
sc.SetName("初一三班");
//獲取班級名稱,顯示“初一三班”
Console.WriteLine(sc.GetName());
//設置學生人數,會拋出ArgumentOutOfRangeException異常
sc.SetNum(-5);
//設置學生人數
sc.SetNum(80);
//輸出80
Console.WriteLine(sc.GetNum().ToString());

在修改后的代碼中,我們將SchoolClass類型中定義的Name和StudentNum字段都設為了private,並且分別為它們定義了兩個方法:一個讀取值一個修改值。這樣我們就可以在SetXXX()方法里面加入邏輯從而保護類型不被破壞掉。這樣做是達到了目的,但是我們必須分別為每一個字段定義GetXXX()和SetXXX()方法,c#為我們提供了無參屬性來方便地達到上述目的,請看下面這段代碼:

public sealed class SchoolClass
    {
        private String m_Name;
        private Int32 m_StudentNum;
        public String Name
        {
            get
            {
                return m_Name;
            }
            set
            {
                m_Name = value;
            }
        }
        public Int32 StudentNum
        {
            get
            {
                return m_StudentNum;
            }
            set
            {
                //關鍵字value總是代表新值,即調用屬性的代碼為屬性賦的值
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException("value", value.ToString(), "人數必須大於0!");
                }
                m_StudentNum = value;
            }
        }
    }

SchoolClass sc = new SchoolClass();
sc.Name = "初一三班";
Console.WriteLine(sc.Name);
sc.StudentNum = 80;
Console.WriteLine(sc.StudentNum);

在這段代碼中我們重新定義了SchoolClass類型,並且為該類型定義了兩個屬性,然后通過訪問屬性來達到了為類型定義私有字段,然后為私有字段定義SetXXX()和GetXXX()方法同樣的目的。我們可以看到屬性都具有get和set訪問器(get和set叫做訪問器),分別用來控制屬性的讀操作和寫操作,當然也可以選擇性地實現get或者set訪問器,只實現get訪問器的屬性稱為只讀屬性,只實現set訪問器的稱為只寫屬性(屬性只能讀不能寫當然毫無意義,當然也通不過c#編譯器的編譯)。說到這里我想對無參屬性應該有一個大致的了解了吧,那下面我們來說一下有參屬性,有了無參屬性的基礎理解有參屬性就容易多了,讓我們來一起看一下:

有參屬性

無參屬性的get訪問器不接受參數,為了彌補這個缺憾,c#開發組提供了有參屬性,有參屬性的get訪問器可以接受一個或多個參數,set訪問器可以接受兩個或多個參數,我們來看看下面這段代碼:

    /// <summary>
    /// 定義一個位數組類型
    /// </summary>
    public sealed class BitArray
    {
        /// <summary>
        /// 位數組長度
        /// </summary>
        private Int32 m_numBits;
        /// <summary>
        /// 位數組
        /// </summary>
        private Byte[] m_byteArray;

        /// <summary>
        /// 構造函數,初始化位數組
        /// </summary>
        /// <param name="numBits"></param>
        public BitArray(Int32 numBits)
        {
            if (numBits < 0)
                throw new ArgumentOutOfRangeException("numBits must be greater than 0");
            m_numBits = numBits;
            m_byteArray = new Byte[(numBits + 7) / 8];
        }

        /// <summary>
        /// c#用this來定義索引器,如果要定義多個,只需要重載this[]就可以了
        /// </summary>
        /// <param name="bitPosition"></param>
        /// <returns></returns>
        public Boolean this[Int32 bitPosition]
        {
            get
            {
                if (bitPosition < 0 && bitPosition >= m_numBits)
                    throw new ArgumentOutOfRangeException("bitPostion must be greater than 0 and less than numBits");
                return (m_byteArray[bitPosition / 8] & (1 << (bitPosition % 8))) != 0;
            }
            set
            {
                if (bitPosition < 0 && bitPosition >= m_numBits)
                    throw new ArgumentOutOfRangeException("bitPostion must be greater than 0 and less than numBits");
                if (value)
                {
                    m_byteArray[bitPosition / 8] = (Byte)(m_byteArray[bitPosition / 8] | (1 << (bitPosition % 8)));
                }
                else
                {
                    m_byteArray[bitPosition / 8] = (Byte)(m_byteArray[bitPosition / 8] & ~(1 << (bitPosition % 8)));
                }
            }
        }
    }

//初始化一個含20個位的BitArray數組
BitArray ba = new BitArray(20);

//調用set訪問器為數組的指定位賦值
for (Int32 i = 0; i < 20; i++)
{
      ba[i] = (i % 2 != 0);
}

//調用get訪問器查看所有的位狀態
for (Int32 i = 0; i < 20; i++)
{
      Console.WriteLine("Bit[" + i + "] is " + ba[i] + "");
}
Console.ReadLine();

 怎么樣?索引器很簡單也很好用吧,下篇博客我們來說說c#中的事件,敬請期待喲!哈哈。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM