使用 lambda 表达式来避免使用“魔术字符串”指定一个属性

发布于 2024-09-11 04:42:18 字数 2690 浏览 3 评论 0 原文

我正在编写一项服务来获取特定类型的对象集合,并且 将其原语、字符串和 DateTime 类型输出CSV 格式。我有以下两种说法。我发现基于 lambda 的版本要干净得多。

魔术字符串版本

string csv = new ToCsvService<DateTime>(objs)
    .Exclude("Minute")
    .ChangeName("Millisecond", "Milli")
    .Format("Date", "d")
    .ToCsv();

与Lambda 版本

string csv = new ToCsvService<DateTime>(objs)
    .Exclude(p => p.Minute)
    .ChangeName(p => p.Millisecond, "Milli")
    .Format(p => p.Date, "d")
    .ToCsv();

根据 Jon Skeet 的建议,所有 lambda 方法都共享相似的方法签名

public IToCsvService<T> Exclude<TResult>(
        Expression<Func<T, TResult>> expression)

然后我将 expression.Body 传递给 FindMemberExpression 。我改编了 中的代码nhlambdaextensions 项目 中 ExpressionProcessor.cs 的 FindMemberExpression 方法。我的 FindMemberExpression 的非常相似的版本如下:

private string FindMemberExpression(Expression expression)
{
    if (expression is MemberExpression)
    {
        MemberExpression memberExpression = (MemberExpression)expression;

        if (memberExpression.Expression.NodeType == ExpressionType.MemberAccess
            || memberExpression.Expression.NodeType == ExpressionType.Call)
        {
            if (memberExpression.Member.DeclaringType.IsGenericType
                && memberExpression.Member.DeclaringType
                .GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if ("Value".Equals(memberExpression.Member.Name))
                {
                    return FindMemberExpression(memberExpression.Expression);
                }

                return String.Format("{0}.{1}",
                    FindMemberExpression(memberExpression.Expression),
                    memberExpression.Member.Name);
            }
        }
        else
        {
            return memberExpression.Member.Name;
        }
    }

    throw new Exception("Could not determine member from "
        + expression.ToString());
}

我正在 FindMemberExpression 中测试足够的案例吗?考虑到我的用例,我所做的事情是否太过分了?

I am writing a service to take a collection of objects of a particular type and output its primitive, string, and DateTime types to a string in CSV Format. I have both of the below statements working. I find the the lambda based version to be much cleaner.

Magic String Version

string csv = new ToCsvService<DateTime>(objs)
    .Exclude("Minute")
    .ChangeName("Millisecond", "Milli")
    .Format("Date", "d")
    .ToCsv();

vs. Lambda Version

string csv = new ToCsvService<DateTime>(objs)
    .Exclude(p => p.Minute)
    .ChangeName(p => p.Millisecond, "Milli")
    .Format(p => p.Date, "d")
    .ToCsv();

Per Jon Skeet's recommendation all of the lambda methods share a similar method signature

public IToCsvService<T> Exclude<TResult>(
        Expression<Func<T, TResult>> expression)

I then pass the expression.Body to FindMemberExpression. I've adapted code from the FindMemberExpression method of ExpressionProcessor.cs from the nhlambdaextensions project. My very similar version of FindMemberExpression is below:

private string FindMemberExpression(Expression expression)
{
    if (expression is MemberExpression)
    {
        MemberExpression memberExpression = (MemberExpression)expression;

        if (memberExpression.Expression.NodeType == ExpressionType.MemberAccess
            || memberExpression.Expression.NodeType == ExpressionType.Call)
        {
            if (memberExpression.Member.DeclaringType.IsGenericType
                && memberExpression.Member.DeclaringType
                .GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if ("Value".Equals(memberExpression.Member.Name))
                {
                    return FindMemberExpression(memberExpression.Expression);
                }

                return String.Format("{0}.{1}",
                    FindMemberExpression(memberExpression.Expression),
                    memberExpression.Member.Name);
            }
        }
        else
        {
            return memberExpression.Member.Name;
        }
    }

    throw new Exception("Could not determine member from "
        + expression.ToString());
}

I am testing for enough cases in FindMemberExpression? Is what I am doing overkill given my use case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陪我终i 2024-09-18 04:42:18

编辑:使这个更简单的核心是将方法的签名也更改为结果类型中的通用:

public IToCsvService<TSource> Exclude<TResult>(
    Expression<Func<TSource, TResult>> expression)

这样您就不会得到转换表达式,因为不需要转换。例如,p =>由于类型推断,p.Minute 将自动以 Expression> 结束。


在我看来,这似乎有点矫枉过正,因为目前您所需要的只是一个属性 - 至少,这就是您的示例所显示的全部内容。

为什么不从识别属性开始,然后在需要时扩展它呢?

编辑:这是一个简短但完整的示例,未显示任何转换:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        Expression<Func<DateTime, int>> dt = p => p.Minute;
        Console.WriteLine(dt);
    }
}

如果将表达式类型更改为 Expression> 但是,它 显示Convert(...) 位。我怀疑您需要更改 Exclude (等)方法的签名。

EDIT: The core to making this simpler is to change the signature of your methods to be generic in the result type too:

public IToCsvService<TSource> Exclude<TResult>(
    Expression<Func<TSource, TResult>> expression)

That way you won't end up with a conversion expression because no conversion will be necessary. For example, p => p.Minute will end up as an Expression<Func<DateTime, int>> automatically due to type inference.


It looks like overkill to me, given that at the moment all you need is a property - at least, that's all that your sample shows.

Why not start off just recognising a property, and expand it later if you need to?

EDIT: Here's a short but complete example which doesn't show any conversions:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        Expression<Func<DateTime, int>> dt = p => p.Minute;
        Console.WriteLine(dt);
    }
}

If you change the expression type to Expression<Func<DateTime, long>> however, it does show the Convert(...) bit. I suspect you need to change the signatures of your Exclude (etc) methods.

如若梦似彩虹 2024-09-18 04:42:18

您是否有计划让它变得更加灵活,或者这就是它需要做的一切?

理想情况下,您应该拥有最简单的代码来完成您需要做的事情,这样您就可以减少可能出错的事情的数量。

如果您这样做是为了概念证明,并且知道稍后您将需要 lambda 表达式,那么将它们保留在那里是有意义的,但是,如果这是最终产品,那么前一个更容易阅读,并且不太可能如果其他人需要更改代码,则会造成混乱。

Do you have any plans on make it more flexible, or is this all that it needs to do?

Ideally you should have the simplest code that will do what you need done, so that you can decrease the number of things that can go wrong.

If you are doing this as a proof of concept, and know that later you will need lambda expressions, then it makes sense to keep them there, but, if this is the final product then the former one is easier to read, and less likely to be a cause of confusion if someone else needs to make changes to the code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文