【问题标题】:Filtering based on PropertyInfo Name基于 PropertyInfo 名称过滤
【发布时间】:2018-03-05 17:49:42
【问题描述】:

我正在尝试修改下面的代码:

public static string ToCsv<T>(this IEnumerable<T> list, List<string> keyIndicators, List<string> inTreatmentCohort)
{
    var type = typeof(T);
    var props = type.GetProperties();

    //Setup expression constants
    var param = Expression.Parameter(type, "x");
    var doublequote = Expression.Constant("\"");
    var doublequoteescape = Expression.Constant("\"\"");
    var comma = Expression.Constant(",");

    //Convert all properties to strings, escape and enclose in double quotes
    var propq = (from prop in props
        let tostringcall = Expression.Call(Expression.Property(param, prop),
            prop.ReflectedType.GetMethod("ToString", new Type[0]))
        let replacecall = Expression.Call(tostringcall,
            typeof(string).GetMethod("Replace", new[] {typeof(string), typeof(string)}), doublequote,
            doublequoteescape)
        select Expression.Call(
            typeof(string).GetMethod("Concat", new[] {typeof(string), typeof(string), typeof(string)}),
            doublequote, replacecall, doublequote)
    ).ToArray();

    var concatLine = propq[0];
    for (var i = 1; i < propq.Length; i++)
        concatLine =
            Expression.Call(
                typeof(string).GetMethod("Concat", new[] {typeof(string), typeof(string), typeof(string)}),
                concatLine, comma, propq[i]);

    var method = Expression.Lambda<Func<T, string>>(concatLine, param).Compile();
    var header = string.Join(",", props.Select(p => p.Name).ToArray());

    return header + Environment.NewLine + string.Join(Environment.NewLine, list.Select(method).ToArray());
}

我需要根据两个列表(keyIndi​​cators 和 inTreatmentCohort)进行过滤,这两个列表包含要转换为 CSV 的列表中包含的 PropertyInfo Name 字段的后缀和前缀。

我尝试添加一个 where keyIndi​​cators.Contains(prop.Name) 本质上模拟一个返回 null 的 IN 语句。

我还尝试将 propq 查询放在 foreach 循环中,这带来了正确的数据,但显示了所有列名,而不仅仅是在 keyIndi​​cators 或 inTreatmentCohort 列表中选择的列名。

如何修改此代码以仅返回包含两个过滤列表中的列而不是列表中正在转换的所有列的 csv?


已修订以包含新的 linq 查询

已经修改了我之前提供的代码,现在可以按预期工作,这些更改可以在下面看到:

public static string ToCsv<T>(this IEnumerable<T> list, List<string> keyIndicators,
    List<string> inTreatmentCohorts)
    {
        var type = typeof(T);
        var props = type.GetProperties();

        //Setup expression constants
        var param = Expression.Parameter(type, "x");
        var doublequote = Expression.Constant("\"");
        var doublequoteescape = Expression.Constant("\"\"");
        var comma = Expression.Constant(",");
        MethodCallExpression[] propq = { };
        var propqList = new List<MethodCallExpression>();

        //Convert all properties to strings, escape and enclose in double quotes


        foreach (var keyIndicator in keyIndicators)
        {
            if (keyIndicator != "Pend" && keyIndicator != "Area" && keyIndicator != "DrugGroup" &&
                keyIndicator != "Gender") continue;
            propq = (from prop in props
                let tostringcall = Expression.Call(Expression.Property(param, prop),
                    prop.ReflectedType.GetMethod("ToString", new Type[0]))
                let replacecall = Expression.Call(tostringcall,
                    typeof(string).GetMethod("Replace", new[] { typeof(string), typeof(string) }), doublequote,
                    doublequoteescape)
                where prop.Name.Contains(keyIndicator)
                select Expression.Call(
                    typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string), typeof(string) }),
                    doublequote, replacecall, doublequote)
            ).ToArray();

            propqList.AddRange(propq);
        }

        foreach (var keyIndicator in keyIndicators)
        {
            foreach (var inTreatmentCohort in inTreatmentCohorts)
            {
                propq = (from prop in props
                         let tostringcall = Expression.Call(Expression.Property(param, prop),
                             prop.ReflectedType.GetMethod("ToString", new Type[0]))
                         let replacecall = Expression.Call(tostringcall,
                             typeof(string).GetMethod("Replace", new[] { typeof(string), typeof(string) }),
                             doublequote,
                             doublequoteescape)
                         where prop.Name.Contains(inTreatmentCohort) && prop.Name.Contains(keyIndicator)
                         select Expression.Call(
                             typeof(string).GetMethod("Concat",
                                 new[] { typeof(string), typeof(string), typeof(string) }),
                             doublequote, replacecall, doublequote)
                ).ToArray();
            }

            propqList.AddRange(propq);
        }

        propq = propqList.ToArray();

        var concatLine = propq[0];
        for (var i = 1; i < propq.Length; i++)
            concatLine =
                Expression.Call(
                    typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string), typeof(string) }),
                    concatLine, comma, propq[i]);

        var method = Expression.Lambda<Func<T, string>>(concatLine, param).Compile();
        var header = string.Join(",", props.Select(p => p.Name).ToArray());

        return header + Environment.NewLine + string.Join(Environment.NewLine, list.Select(method).ToArray());
    }

我需要为 var header 重写 LINQ 查询,以便在生成 csv 时只显示在 propq 中选择的列。

【问题讨论】:

  • 请同时展示您的尝试,例如您对Contains 的尝试应该有效。

标签: c# linq extension-methods system.reflection


【解决方案1】:

在做了一些修改后,我设法让 Contains 与几个 foreach 循环一起工作,以处理两个过滤器列表。

完整代码如下:

public static string ToCsv<T>(this IEnumerable<T> list, List<string> keyIndicators,
    List<string> inTreatmentCohorts)
    {
        var type = typeof(T);
        var props = type.GetProperties();

        //Setup expression constants
        var param = Expression.Parameter(type, "x");
        var doublequote = Expression.Constant("\"");
        var doublequoteescape = Expression.Constant("\"\"");
        var comma = Expression.Constant(",");
        MethodCallExpression[] propq = { };
        var propqList = new List<MethodCallExpression>();
        var columnNames = new List<string>();

        //Convert all properties to strings, escape and enclose in double quotes

        foreach (var keyIndicator in keyIndicators)
        {
            foreach (var inTreatmentCohort in inTreatmentCohorts)
            {
                propq = (from prop in props
                    let tostringcall = Expression.Call(Expression.Property(param, prop),
                        prop.ReflectedType.GetMethod("ToString", new Type[0]))
                    let replacecall = Expression.Call(tostringcall,
                        typeof(string).GetMethod("Replace", new[] {typeof(string), typeof(string)}), doublequote,
                        doublequoteescape)
                    where prop.Name.Contains(keyIndicator) && prop.Name.Contains(inTreatmentCohort)
                    select Expression.Call(
                        typeof(string).GetMethod("Concat", new[] {typeof(string), typeof(string), typeof(string)}),
                        doublequote, replacecall, doublequote)
                ).ToArray();

                var columnNameQuery = (from prop in props
                    where prop.Name.Contains(keyIndicator) && prop.Name.Contains(inTreatmentCohort)
                    select prop.Name);

                columnNames.AddRange(columnNameQuery);

                propqList.AddRange(propq);
            }
        }

        propq = propqList.ToArray();

        var concatLine = propq[0];
        for (var i = 1; i < propq.Length; i++)
            concatLine =
                Expression.Call(
                    typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string), typeof(string) }),
                    concatLine, comma, propq[i]);

        var method = Expression.Lambda<Func<T, string>>(concatLine, param).Compile();
        var header = string.Join(",", columnNames.ToArray());

        return header + Environment.NewLine + string.Join(Environment.NewLine, list.Select(method).ToArray());
    }

我希望包含在 CSV 中的列名被添加到 columnNames 列表中,然后被添加到标题变量中,以便列和数据在 CSV 中匹配。

【讨论】:

    【解决方案2】:

    尝试以下:

                string[] columnNames = { "A", "B", "C", "D" };
                string[] filteredColumns = { "A", "C", "D" };
    
                string[] csv = {
                                   "1,2,3,4",
                                   "11,12,13,14",
                                   "21,22,23,24",
                                   "31,32,33,34"
                               };
    
                int[] indexes = columnNames.Select((x, i) => new { x = x, i = i }).Where(x => filteredColumns.Contains(x.x)).Select(x => x.i).ToArray();
    
                var results = csv.Select(x => x.Split(new char[] { ',' }).Where((y, i) => indexes.Contains(i)).ToArray()).ToArray();
    

    【讨论】:

    • 你的很多答案都是由破英语这两个词组成的,“Try following”,还有一大堆代码。按照 Stack Overflow 的标准,这些都不是很好的答案。你应该解释你的解决方案是做什么的,以及为什么它是一个好的解决方案,而不是把一大堆代码放到网站上,然后期望人们根据你的意图去挖掘它。
    猜你喜欢
    • 2020-07-04
    • 2012-05-14
    • 2018-06-17
    • 2022-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多