C# 4.0新特性-索引屬性

首先祝大家元旦快樂(有朋友祝福我新年快樂,但總覺得沒那個味道,我心中的新年快樂一直都是春節快樂的意思,不過聽Happy New Year又能接受~~)。這篇文章我主要給大家介紹一個C# Team很少提及的一個關於C# 4.0的新特性--索引屬性。文章主要從索引屬性是什麼,爲什麼需要它,爲什麼C#又不完全支持它,目前C#索引屬性的實現以及怎樣使用它四個方面進行介紹。

什麼是索引屬性

望文生義,索引屬性就是指能根據索引值返回指定結果的屬性,它是訪問屬性的原子操作,如someObject.someProperty[index]。索引化屬性跟普通屬性差不多,唯一不同的是它多了一個額外的表示索引值的參數。這個新的特性加入到C# 4.0中比較晚,它在VS 2010 Beta1中都還沒有,是在Beta2中才加入的新特性。

爲什麼我們需要它?

如果你經常跟COM打交道,你應該應該會遇到類似下面的代碼(這段代碼還是Scott Hanselman 在Beta1發佈時候寫的例子:):

var excel = new Excel.Application();
excel.Visible = true;
excel.Workbooks.Add();
excel.get_Range("A1").Value2 = "Process Name";
excel.get_Range("B1").Value2 = "Memory Usage";

這段代碼還僅僅是一部分,實際上你可能會碰到更難纏的代碼。e.g.

myObject.set_IndexedProperty(myObject.get_IndexedProperty(index) + 1)。它看起來真的很臃腫,爲什麼我們不可以這樣來調用呢?myObject.IndexedProperty[index]++。
作爲一個存在已久的模塊,COM裏面有很多很多像上面這種屬性操作方法,它也使得程序員寫出的與COM交互的代碼看起來臃腫不堪。爲了解決這種狀況,C# 4.0中提出了很多改進C#與COM交互的方式,
dynamic是其中一種,而這裏C# Team則通過索引屬性改善上面提到的這些狀況,它使得程序員寫出的代碼更爲簡潔。

爲什麼C#不支持定義索引屬性

然而,儘管索引屬性可以簡化代碼,但是,目前C#對它的支持卻極其有限,它只能用於C#調用COM中的對象纔會出現,你不可以在C#中聲明和調用索引屬性。並且其實現並未從編譯器或CLR中實現,而僅僅是通過語法糖來簡單實現而已。這又是爲什麼呢?

C# Team給出的答案是C#中對象不同的職責的分離,理由如下:

屬性是用來獲取對象的,它從屬於其父對象。

索引值是用在屬性返回的對象中枚舉它的,它應該從屬於返回的對象而不是屬性的父對象。

鑑於這種原因,C#一直不打算在C# 4.0 中實現這個特性,這個新的特性無論是在C# 4.0 language specification還是C# Future page都沒有被提到,它看起來更像是C# Team對不得不與COM交互的某種暫時性的妥協而已。實際上照C# Team的想法,他們其實早就給出了他們認爲是正確的索引屬性的使用方式,讀者如果有看VS 2008 目錄下的Sample文件夾的話,你可以看到裏面有個專門的indexedproperty的例子,在2010 beta2中的Sample中它則變成了Indexers_2,不過兩者代碼是一樣的。另外,MSDN上這篇文章也給出了例子,這裏我就不給出詳細實現,而給出一個類似的例子。首先我們定義一個帶索引器的集合類e.g.

public SomeCollection 
{
    public int this[int index] { get{//bla} set{//bla} }
 }
 public class IndexerTest 
{
    public SomeCollection somePropertys{ get; set; }
    public int[] otherPropertys{get;set;}
 }

然後你可以這樣使用:

class Program
{
      static void main(){
         IndexTest it=new IndexTest();
         int a=it.somePropertys[0];
          int b=it.otherPropertys[0];
       }
}

從這些代碼不難看出C# Team對索引屬性的觀點,他們認爲索引器的職責應該屬於屬性返回的對象的,而不是屬於該屬性的父對象,父對象無法通過索引值來控制屬性返回值。你添加到屬性上的索引其實是對集合或者數組操作,而不是由IndexTest在自己內部控制。從這個觀點上將,我們就不難理解爲什麼C# 4.0僅僅支持COM的索引屬性了。

索引屬性的實現

目前的C# 4.0並不支持聲明索引屬性,它僅僅在COM中使用,在用戶引用COM對象的時候,編譯器會從COM類型中(標有tdImport標誌的類型)導入所有看起來像索引屬性的成員,然後提供給用戶。通過這種方式,用戶現在可以通過屬性索引器語法形式來訪問這些成員。下面我們根據上面的例子來看看具體過程。

當編譯器碰到索引屬性的情況時,它會綁定.號左邊的對象即it,然後它會獲取it的對象類型即IndexTest,接着,它會去IndexTest類中查找所有名稱爲somePropertys的成員。

爲了向後兼容,編譯器優先考慮的是屬性名匹配,而不去考慮這個屬性返回值是不是真的支持索引屬性。從這個角度來看,索引屬性有點像擴展方法,只有當正常的方法匹配都失效的時候,它們纔會被用到。舉例來說吧,如果編譯器在IndexTest中找到了一個普通的屬性名字叫做somePropertys,它仍認爲這個屬性就是用戶想要調用的,即使這個屬性somePropertys並沒有實現索引器或不是一個數組。接着,當編譯器發現這個屬性的返回值並不支持索引器,這時候匹配失敗,編譯器會報錯。

當編譯器找不到正常的屬性跟待查找的名字一致時候,它會去檢查IndexTest是不是COM類型,如果是的話,它會用與重載函數類似的匹配方式在所有符合條件的索引屬性中找到匹配的成員,這時候它查找的其實是get_屬性名(index)這種格式,如果能找到,編譯器則把匹配的方法當做索引化屬性調用。e.g. 上例中的索引屬性其實IL代碼類似IndexTest.get_somePropertys(index).

索引屬性的使用

說到底,其實索引屬性只是個語法糖而已,編譯器和CLR跟以前比並沒有什麼變化,雖然如此,不過它確實能簡化我們代碼的書寫。所有以前你在與COM交互時候調用get_X()或者set_X()的方法,現在都可以通過X[]了,e.g.

// C# 3.0
excel.get_Range("A1").set_Value(Type.Missing, "ID");
// C# 4.0
excel.Range["A1"].Value = "ID";
還是上面提到的Scott Hanselman在Beta1發佈時候寫的例子,在Beta2之後我們可以更簡單了:
var excel = new Excel.Application();
excel.Visible = true;
excel.Workbooks.Add();
excel.Range["A1"].Value = "Process Name";
excel.Range["B1"].Value = "Memory Usage";

代碼是不是簡化很多?雖然上面代碼本質上是一樣的,不過它確實看起來更加簡潔。如果你需要經常與COM打交道,相信你應該會很喜歡索引屬性的。另外,目前索引屬性不僅支持靜態代碼,還支持動態代碼。

總結

由於C# Team並不想在C#中加入索引屬性這個特性支持(儘管VB支持),你可以參考關於Anders大大的這篇採訪視頻, 它是在後期由於要求對COM有更好的支持才被迫加上的。所以這個特性並沒有在編譯器或CLR內部做什麼大的改變,它僅僅是個語法糖,並且實現得很有限。首先,它只能用在COM中,在普通的C#代碼中你無法聲明和定義這個特性,所以這也導致它的使用範圍相當有限,可能很多人甚至都不會接觸到;其次,它只是讓用戶看起來像調用索引屬性,實際上則是通過get_X和set_X方法獲取屬性。最後,爲了向後兼容,使得以前舊的代碼仍能正常工作,你也可以選擇繼續使用get_X()和set_X()方法來操作屬性。

希望本文能讓你對索引屬性有所瞭解!

參考文章:

1. Com interop in C# 4.0: Indexed Properties 作者: samng, 地址:http://blogs.msdn.com/samng/archive/2009/11/03/com-interop-in-c-4-0-indexed-properties.aspx

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