[LINQ] 開發系列(1)、如何在方法中傳回匿名型別

  • 10095
  • 0
  • C#
  • 2012-04-24

為什麼需要在方法中傳回匿名型別?如果你查了許多相關的資料後會發現,雖然 var 也是強型別的一種,但是 var 只能在方法中宣告、使用,所以通常你不能在方法的回傳型別裡直接宣告 var ,但這是不是表示就不能透過方法回傳匿名型別?其實,也不盡然...

什麼是匿名型別?在 91大最近的一篇  [.NET]快快樂樂學LINQ系列前哨戰-var與匿名型別 文章中我想解說得非常的清楚,這裡我就不在詳敘,而為什麼會有這樣的需求,我們由下分解。

來由:

讀者一定也覺得奇怪。需要在方法中傳回匿名型別?說到匿名型別,如果你查了許多相關的資料後會發現,雖然 var 也是強型別的一種,但是 var 只能在方法中宣告、使用,所以通常你不能在方法的回傳型別裡直接宣告 var ,但這是不是表示就不能透過方法回傳匿名型別?其實,也不盡然,各位不要忘了!var 是由編譯器在編器的時候推斷其型別,這表示它最終有其型別存在,所以這表示什麼?它一定某種型別,只是它是種 object 弱型別的物件,而重點是,弱型別我們還是不知道它在 Runtime 的時候究竟便成什麼型態的物件,前面提到過,對 var 而言,在 Runtime 的時候編譯器早已清楚它的型態,那不是一樣可以透過泛型來傳?甚至讓我得知這個暱名物件的內容。是的!沒錯,正是如此!筆者先以一個簡單的 Console 專案來測試。

首先,使用一個以 object 回傳匿名型別的一個方法,在這個回傳的暱名型別中,共三個物件,有一個 int 另外兩個 string ,如下,直接取用我們無法在 IntelliSense 看到其成員,甚至編譯時,編譯器就會告知您根本找不到這個屬性,如下;

image

但是如同最上面筆者所說,如果透過泛型來轉換呢?筆者另外在寫一個 static T Cast<T>(object sourceObj, T destType) 方法,sourceObj 當然就是帶入 ReturnAnonymous() ,而 T 的 destType 就是傳入一個實際的匿名型別的 Pattern ,簡單的說就是一個實際的匿名型別的實體,帶入到泛型中,不過這不是正規的做法,筆者在 4/21 OpenDay  那天也有與 91 哥討論過這個問題,因為泛型方法的 Cast<T> 有需要帶入的 T 型別,但在這裡我們並不知道其型別會是什麼,所以在呼叫 Cast 方法的時候我們無法該方法使用何種 T ,但是可以在傳入的 T destType 的 Runtime 型別來告知編譯器 T 是什麼。起先筆者也是有一點疑問,只是透過傳入的 T ,真的就可以告知其內容是什麼嗎?有時還是可以大膽的嘗試一下自己的理論是否可行,於是,二話不說,寫寫看。如下:

image

實際測試的時後發現,我們連在 IntelliSense 都可以查閱其匿名型別的內容,到了這裡,筆者總算燃起一些希望,雖然 object 是弱型別,但我已經可以透過方法來回傳了。那麼執行呢?我們來看看執行的結果,如下:

image

果然,程式也能夠順利的執行。為了在一次證明可以透過傳入的 T type 來推斷其回傳的匿名型別,我們再來做一個簡單的實驗。由於我們知道我們在使用 LINQ 時,如果要傳回匿名型別可以使用 IEnumerable<T> 來回傳

   1:          public IEnumerable<T> GetTestORGData<T>(T type)
   2:          {
   3:              CDC_LABEntities context = new CDC_LABEntities();
   4:              var result = from org in context.ORG_DATA
   5:                           select new
   6:                           {
   7:                               OID = org.OID,
   8:                               O_Name = org.O_name
   9:                           };
  10:              return (IEnumerable<T>) result.ToList();
  11:          }

 

 

,如果我們不給 T 的型態,或是了錯誤的型態,Visual Studio 2010 也會很聰明回應你該型別不能用這種方式推斷,如下:

image

如果你給的是不正確的,仍然可以編譯通過,但是在 Runtime 的時候你會得到意想不到的結果!呃.. 應該是意想不到的錯誤才對。

image

當然,我們如果是傳入正確的匿名型態到 T 中,型別就會推斷出回傳的匿名型態是什麼,如下:

image

 

其實筆者真正要解決的問題是,要將某個現有的 DataTable 轉換為匿名型別,由於我們知道在 .NET 3.0/3.5 之後 DataTable 等串列物件均實做了AsEnumerable() 方法,如果我們要將該 DataTable 中的資料擷取某些欄位,並回傳匿名型別的物件,套用進去後程式變成如下:

image

同樣的,透過傳入的 T 泛型物件推斷其型別,在呼叫  GetAnonymousByRuleColumns() 的時候我們必須傳入該匿名實體,該匿名實體的實際內容可以只給空直即可,因為目的只是為了可以推斷其型別,達到可接取匿名型別的目的。

image

果然,我們可以在下方的 foreach 中正確的取得該 DataTable 中的資料,並且是以匿名型別的方式。

 

結論:

透過型別推斷雖然幫我們解決了一個問題,但筆者認為這樣的做法其實也讓程式便的不容易維護 (在方法參數中傳遞一個空的匿名挺怪的),不太適合大量的使用。但透過這個小實驗也讓我們對於.NET 的型態推斷、匿名型別 等又有一些些的認識,而在 LINQ  開始被大量使用的時候實際運的時候,有時也確實會需要回傳一個動態的匿名型別,那麼或許這也是一種解法。


 

簽名:

學習是一趟奇妙的旅程

這當中,有辛苦、有心酸、也有成果。有時也會有瓶頸。要能夠繼續勇往直前就必須保有一顆最熱誠的心。

軟體開發之路(FB 社團)https://www.facebook.com/groups/361804473860062/

Gelis 程式設計訓練營(粉絲團)https://www.facebook.com/gelis.dev.learning/


 

如果文章對您有用,幫我點一下讚,或是點一下『我要推薦,這會讓我更有動力的為各位讀者撰寫下一篇文章。

非常謝謝各位的支持與愛護,小弟在此位各位說聲謝謝!!! ^_^