工作的專案有個小需求,使用者羅列了一堆報表匯出需求,基上都是從現存LINQ資料集合以不同條件取出不同欄位。
我想到最簡便的做法是套用Where()查詢,依需求產生匿名型別
Select(o => new {
欄位1 = o.PropA,
欄位2 = o.PropB,
欄位3 = o.PropC …
})
連欄位名稱都隨使用者指定,最後再將查詢結果轉為CSV,幾個步驟就搞定一項報表需求,進行量產。
基於以上構想,我需要一個能將任意匿名型別陣列或List自動轉成CSV的共用函數,挑戰點在於陣列元素型別未知,但這可難不倒C#,祭出Reflection就能輕鬆克服。
試寫範例如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace AnonymousTypeListReflectionTest
{
class Program
{
staticvoid Main(string[] args)
{
var ary = "Jeffrey,Darkthread,Geek".Split(',')
.Select(o => new
{
文字 = o,
長度 = o.Length
}).ToArray();
Console.Write(convToCsv(ary));
Console.Read();
}
//將任意型別陣列輸出為CSV,第一列為標題列舉欄位名稱
staticstring convToCsv(Array ary)
{
//取得陣列元素的型別
Type elemType = ary.GetType().GetElementType();
PropertyInfo[] props = elemType.GetProperties();
StringBuilder sb = new StringBuilder();
//第一列輸出屬性名稱
sb.AppendLine(string.Join("\t", props.Select(o => o.Name).ToArray()));
//藉由foreach巡迴每一元件,透過Refelction取出屬性值
foreach (object elem in ary)
{
sb.AppendLine(string.Join("\t",
props.Select(o => Convert.ToString(o.GetValue(elem, null)))));
}
return sb.ToString();
}
}
}
實際寫完,程式遠比預期來得單純: 傳入參數時,將匿名型別陣列轉型成Array、GetElementType()可以找出陣列元素型別、Reflection GetProperties()跟PropertyInfo.GetValue()已是老把戲不用多說,至於分隔符號我選擇用Tab "\t"取代逗號,以省去處理屬性值內含逗號的麻煩。
就這樣,任意型別陣列轉CSV的函數就寫完囉~ 又到了感恩時刻,讓我們一起高呼: .NET好威呀!