9

汎用リストを受け入れるユーザー コントロールを作成しようとしているので、それを反復処理して CSV エクスポートを作成できます。任意の型を受け入れることができるパブリック プロパティを公開することは可能ですか? (つまりList<Product>List<Customer>など) はいの場合、どのように?

public IEnumerable<T> AnyList { get; set; }

私が持っているユーティリティメソッドに関する限り、私が持っているものは次のとおりです。

public static byte[] ToCsv<T>(string separator, IEnumerable<T> objectlist)
{
    //Type t = typeof(T); Deleted this line.

    //Here's the line of code updated.
    PropertyInfo[] propertyNames = objectlist.First().GetType().GetProperties();

    string header = String.Join(separator, propertyNames.Select(f => f.Name).ToArray());

    StringBuilder csvdata = new StringBuilder();
    csvdata.AppendLine(header);

    foreach (var o in objectlist)
        csvdata.AppendLine(ToCsvFields(separator, propertyNames, o));

    return Encoding.ASCII.GetBytes(csvdata.ToString());
}

public static string ToCsvFields(string separator, PropertyInfo[] fields, object o)
{
    StringBuilder linie = new StringBuilder();

    foreach (var f in fields)
    {
        if (linie.Length > 0)
            linie.Append(separator);

        var x = f.GetValue(o, null);

        if (x != null)
            linie.Append(x.ToString());
    }

    return linie.ToString();
}
4

6 に答える 6

17

オブジェクトのリストから「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);
}

私は実際には一般的な解決策を好むので、それを最初に置きました。

于 2012-06-06T14:34:28.577 に答える
2

次のようにする必要があります。

public class MyUserControl<T> : UserControl
{
    public IEnumerable<T> AnyList { get; set; }
}

C# の観点からは、これで問題ありません。ただし、ASPX マークアップでユーザー コントロールを宣言できるようにする場合、ユーザー コントロールにクラス レベルの型パラメーターを含めることはできません。これは不可能です:

<controls:MyUserControl<Customer> runat="server" ID="Foo" />

ここで、ユーザー コントロールがコード ビハインドでのみ作成され、マークアップでは作成されない場合は、これで問題ありません。

this.SomePlaceHolder.Controls.Add(new MyUserControl<Customer>());

ASPX マークアップでユーザー コントロールを宣言する機能が必要または必要な場合は、IEnumerable<object>プロパティを作成することをお勧めします。

于 2012-06-06T14:14:07.463 に答える
1

私はここから素晴らしい解決策を見つけました

public static class LinqToCSV
{
    public static string ToCsv<T>(this IEnumerable<T> items)
        where T : class
    {
        var csvBuilder = new StringBuilder();
        var properties = typeof(T).GetProperties();
        foreach (T item in items)
        {
            string line = string.Join(",",properties.Select(p => p.GetValue(item, null).ToCsvValue()).ToArray());
            csvBuilder.AppendLine(line);
        }
        return csvBuilder.ToString();
    }

    private static string ToCsvValue<T>(this T item)
    {
        if(item == null) return "\"\"";

        if (item is string)
        {
            return string.Format("\"{0}\"", item.ToString().Replace("\"", "\\\""));
        }
        double dummy;
        if (double.TryParse(item.ToString(), out dummy))
        {
            return string.Format("{0}", item);
        }
        return string.Format("\"{0}\"", item);
    }
}

どうやらそれはコンマと二重引用符を処理します。使ってみます。

于 2013-02-14T02:17:58.603 に答える
0

ジェネリック プロパティを持つには、クラスもジェネリックである必要があります。

public class MyClass<T>
{
   public IEnumerable<T> AnyList { get; set; }
}
于 2012-06-06T14:10:30.850 に答える