オブジェクトのリストから「CSV エクスポートを作成」したいので、リフレクションを使用して列を作成する必要があります。
2016 年 2 月 12 日の最新更新:
区切り文字のデフォルトをコンマにする方が理にかなっており、最初のヘッダー行の出力をオプションにするのに便利です。また、以下を使用して、フィールドと単純なプロパティConcat
の両方をサポートするようになりました。
public static IEnumerable<string> ToCsv<T>(IEnumerable<T> objectlist, string separator = ",", bool header = true)
{
FieldInfo[] fields = typeof(T).GetFields();
PropertyInfo[] properties = typeof(T).GetProperties();
if (header)
{
yield return String.Join(separator, fields.Select(f => f.Name).Concat(properties.Select(p=>p.Name)).ToArray());
}
foreach (var o in objectlist)
{
yield return string.Join(separator, fields.Select(f=>(f.GetValue(o) ?? "").ToString())
.Concat(properties.Select(p=>(p.GetValue(o,null) ?? "").ToString())).ToArray());
}
}
したがって、カンマ区切りで次のように使用します。
foreach (var line in ToCsv(objects))
{
Console.WriteLine(line);
}
または、別の区切り文字 (TAB など) の場合は次のようにします。
foreach (var line in ToCsv(objects, "\t"))
{
Console.WriteLine(line);
}
実践例
リストをコンマ区切りの CSV ファイルに書き込みます
using (TextWriter tw = File.CreateText("C:\testoutput.csv"))
{
foreach (var line in ToCsv(objects))
{
tw.WriteLine(line);
}
}
またはタブ区切りで書く
using (TextWriter tw = File.CreateText("C:\testoutput.txt"))
{
foreach (var line in ToCsv(objects, "\t"))
{
tw.WriteLine(line);
}
}
複雑なフィールド/プロパティがある場合は、select 句から除外する必要があります。
以前の更新
最初に最終的な考え(見逃さないように):
一般的な解決策を好む場合 (これにより、オブジェクトが同じ型であることが保証されます):
public static IEnumerable<string> ToCsv<T>(string separator, IEnumerable<T> objectlist)
{
FieldInfo[] fields = typeof(T).GetFields();
PropertyInfo[] properties = typeof(T).GetProperties();
yield return String.Join(separator, fields.Select(f => f.Name).Union(properties.Select(p=>p.Name)).ToArray());
foreach (var o in objectlist)
{
yield return string.Join(separator, fields.Select(f=>(f.GetValue(o) ?? "").ToString())
.Union(properties.Select(p=>(p.GetValue(o,null) ?? "").ToString())).ToArray());
}
}
これには、パブリック フィールドとパブリック プロパティの両方が含まれます。
一般に、リフレクションでは、リスト内のオブジェクトのタイプを知る必要はありません (すべて同じタイプであると仮定する必要があります)。
あなたはただ使うことができます:
public IEnumerable<object> AnyList { get; set; }
やりたいことの基本的なプロセスは次のようになります。
- リストの最初のオブジェクトから型を取得します (例:
GetType()
)。
- その型のプロパティを繰り返します。
- たとえば、プロパティ (または属性) の名前に基づいて、CSV ヘッダーを書き出します。
- リスト内の各項目について...
- その型のプロパティを反復する
- 各プロパティの値を取得します (オブジェクトとして)
- オブジェクトの ToString() バージョンを区切り記号で書き出す
同じアルゴリズムを使用して 2D 配列を生成できます (つまり、コントロールに CSV のようなものを表形式/グリッド形式で表示させたい場合)。
あなたが抱えている唯一の問題は、IEnumerables/特定の型のリストから IEnumerable への変換かもしれません。これらのインスタンスでは.Cast<object>
、特定の型付き列挙型で使用するだけです。
アップデート:
http://www.joe-stevens.com/2009/08/03/generate-a-csv-from-a-generic-list-of-objects-using-reflection-and-extension-のコードを使用しているためメソッド/
彼のコードに次の変更を加える必要があります。
// Make it a simple extension method for a list of object
public static string GetCSV(this List<object> list)
{
StringBuilder sb = new StringBuilder();
//Get the properties from the first object in the list for the headers
PropertyInfo[] propInfos = list.First().GetType().GetProperties();
空のリストをサポートしたい場合は、list.First().GetType() の代わりに、期待Type type
したオブジェクトのタイプである 2 番目のパラメーター (例: ) を追加して使用します。
注: T が参照されている彼のコードの他の場所は見当たりませんが、それを見逃した場合は、コンパイラがそれを見つけてくれます :)
更新 (完全で簡素化された CSV ジェネレーター):
public static IEnumerable<string> ToCsv(string separator, IEnumerable<object> objectlist)
{
if (objectlist.Any())
{
Type type = objectlist.First().GetType();
FieldInfo[] fields = type.GetFields();
yield return String.Join(separator, fields.Select(f => f.Name).ToArray());
foreach (var o in objectlist)
{
yield return string.Join(separator, fields.Select(f=>(f.GetValue(o) ?? "").ToString()).ToArray());
}
}
}
これには、大量の文字列を作成するのではなく、結果が発生したときに結果が得られるため、メモリ オーバーヘッドが低いという利点があります。次のように使用できます。
foreach (var line in ToCsv(",", objects))
{
Console.WriteLine(line);
}
私は実際には一般的な解決策を好むので、それを最初に置きました。