屬性分兩種,無參屬性,有參屬性,後者又叫索引器(indexer)——VB.NET中相應為默認屬性。
1.無參屬性
CLR支持靜態屬性,實例屬性,抽象屬性,虛擬屬性,但不能被重載。
屬性在MSIL中生成以下內容:
get_XX方法,當在屬性中定義了get的時候——XX為屬性名
set_XX方法,當在屬性中定義了set的時候——XX為屬性名
一個位於原數據中的屬性定義。
屬性不能作為out/ref傳遞給方法,字段則可以。
如果屬性中執行語句過多,要花很多時間,這時候優先選用方法。例如線程同步和Remoting。
多次訪問屬性,每次返回的值可能不同;而字段的值每次返回都不會改變。例如System.DateTime屬性 ——MS以後會將其改為方法。
2.有參屬性
定義,有參屬性的get方法可以接受1個或者更多參數,set方法可以接受2個或者更多參數
——對比,無參屬性get方法無參數,set方法有1個參數。
C#索引器語法,是對[ ]這個運算符的重載,示例如下:
public class FailSoftArray { int[] a; public int length; public FailSoftArray(int length) { a = new int[length]; this.length = length; } public int this[int index] { get { if (index >= 0 & index < length) { return a[index]; } else throw new ArgumentOutOfRangeException(); } set { if (index >= 0 & index < length) { a[index] = value; } else throw new ArgumentOutOfRangeException(); } } } public class TestIndex { public TestIndex() { FailSoftArray fs = new FailSoftArray(5); for (int i = 0; i < 5; i++) { fs[i] = i * 10; } } }
索引器是可以重載的,不能在this[]的類型上重載,只能在參數上重載——無參屬性不可以重載:
public int this[int index] { set { } } public int this[double index] { set { } }
以下索引器重載是不對的,盡管改變了Item,在MSIL中是有不同方法名的兩套方法,卻具有相同的參 數集;但是C#不是按名稱引用索引器,而是按參數集。
[IndexerName("Jeff")] public String this[bool b] { set { } } [IndexerName("Jax.Bao")] public Int32 this[bool b] { set { } }
CLR不區分無參屬性和有參屬性,都是生成一對方法和一塊元數據:
get_Item方法,當在屬性中定義了get的時候
set_Item方法,當在屬性中定義了set的時候
一個位於原數據中的屬性定義。在MSIL中,有專門用於有參屬性的元數據定義表。
對於索引器,使用get_Item和set_Item這兩個默認名稱。同時,通過反射查看類型是否有Item屬性, 來判斷該類型是否提供了索引器(Generic.List)。
但是,C#編程中不會看到Item這一默認屬性,在與其它語言交互時,可能需要把Item改為其他的名稱 ,方法如下:
using System.Runtime.CompilerServices; public sealed class BitArray { [IndexerName("Bit")] public bool this[int bitPos] { set { } } }
於是,MSIL生成get_Bit和set_Bit方法。NET中的String.String類型就是Chars而不是Item。
索引器語法this[..],是C#團隊選擇的表示方式,意味著C#只允許在對象的實例上定義索引器。
CLR支持靜態有參屬性,但是C#沒有提供相關語法
補充:VB.NET"默認屬性"語法
將C#的索引器示例改造成VB.NET的:將屬性聲明為Default,並加上參數,只是這裡的this不再是關鍵 字,而是一個自定義的屬性
Public Class FailSoftArrayClass FailSoftArray Public length As Integer Private a(length) As Integer Public Sub New()Sub New(ByVal length As Integer) Me.length = length End Sub Default Public Property Name()Property this(ByVal index As Integer) Get If (index >= 0 & index < length) Then Return a(index) Else Throw New ArgumentOutOfRangeException() End If End Get Set(ByVal value) If (index >= 0 & index < length) Then a(index) = value Else Throw New ArgumentOutOfRangeException() End If End Set End Property End Class Public Class TestIndexClass TestIndex Public Sub New()Sub New() Dim fs As FailSoftArray = New FailSoftArray(5) Dim i As Integer For i = 1 To 5 fs(i) = i * 10 Next End Sub End Class
3.性能
內聯:就是方法下包括全部代碼,不需要將部分代碼放到額外的調用函數中——消除了運行時與調用 相關的開銷,代價是編譯後方法的代碼很龐大。
內聯屬性方法,因為其通常包括極少代碼,所以編譯後代碼量更小,執行更快。
由於JIT調試時期不會內聯屬性方法,所以屬性訪問器在Release版本性能要快於Debug版本——字段在 兩個版本中執行得同樣快。
4.屬性訪問器的可訪問性
可以get是public的;而set是protected的
get/set的可訪問性是在屬性名前聲明的
public String Name //這裡的public決定了get/set默認的可訪問性 { get { return null; } protected set { } //這裡提升了set的可訪問性 }
5.雖然屬性實際上就是方法,但是不允許泛型屬性。