高効率IEnumerable回転データテーブル

3180 ワード

IEnumerableのTは汎用型であり,Tにどのような属性があるかを事前に知ることはできないため,作成したDataTableでもカラムを予め設定することはできない.このような場合、まず反射を思い浮かべます.
public static DataTable ToDataTable(IEnumerable collection)
{
    var props = typeof(T).GetProperties();
    var dt = new DataTable();
    dt.Columns.AddRange(props.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray());
    if (collection.Count() > 0)
    {
        for (int i = 0; i < collection.Count(); i++)
        {
            ArrayList tempList = new ArrayList();
            foreach (PropertyInfo pi in props)
            {
                object obj = pi.GetValue(collection.ElementAt(i), null);
                tempList.Add(obj);
            }
            object[] array = tempList.ToArray();
            dt.LoadDataRow(array, true);
        }
    }
    return dt;
}

反射効率が低く、自然と表現ツリーというものを思い浮かべます.表現ツリーの1つの役割は、反射の機能を実現し、反射による効率の問題を回避することです.
まずobj=>objのような形を構築するつもりです.Propertyの式ツリー.
//      Func getAge = u => u.Age; 
static Func GetGetDelegate(PropertyInfo p)
{
    var param_obj = Expression.Parameter(typeof(T), "obj");
    //lambda     u.Age
    var pGetter = Expression.Property(param_obj, p);
    //  lambda
    return Expression.Lambda>(pGetter, param_obj).Compile();
}

static object FastGetValue(this PropertyInfo property, T t)
{
    return GetGetDelegate(property)(t);
}

次に、上記の反射コードを次のように変更できます.
public static DataTable ToTable(this IEnumerable collection)
{
    var props = typeof(T).GetProperties();

    var dt = new DataTable();
    dt.Columns.AddRange(
        props.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray()
    );

    collection.ToList().ForEach(
        i => dt.Rows.Add(props.Select(p => p.FastGetValue(i)).ToArray())
    );

    return dt;
}

いいですね.反射することなく仕事を完成しました.しかし、効率は本当に向上していますか?必ずしもそうではないと思います.後者は前者のpiをGetValue(collection.ElementAt(i),null)はp=>p.FastGetValue(i)に変更され、FastGetValueは毎回式ツリーを構築してLambdaをコンパイルし、IEnumerableの各データに対して同じ属性依頼を繰り返し構築します.両者の効率は私は測ったことがありませんが、この方法は完璧ではありません.改善された方法は、属性とGetGetDelegate構造の委任をキー値ペアとしてキャッシュして後続のループで使用するなど、多くあります.次は私が考えている良い解決方法です.
 
 static Func GetGetDelegate(PropertyInfo[] ps)
 {
     var param_obj = Expression.Parameter(typeof(T), "obj");
     Expression newArrayExpression = Expression.NewArrayInit(typeof(object), ps.Select(p => Expression.Property(param_obj, p)));
     return Expression.Lambda>(newArrayExpression, param_obj).Compile();
 }
public static DataTable ToTable(this IEnumerable collection)
{
    var props = typeof(T).GetProperties();
    var func = GetGetDelegate(props);
    var dt = new DataTable();
    dt.Columns.AddRange(
        props.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray()
    );
    collection.ToList().ForEach(i => dt.Rows.Add(func(i)));

    return dt;
}

転載は本文の出典を明記してください.http://www.cnblogs.com/newton/archive/2013/01/09/2853083.html