使用lambda表达式来避免使用“魔术字符串”以指定属性 [英] Using a lambda expression to avoid using a "magic string" to specify a property

查看:1233
本文介绍了使用lambda表达式来避免使用“魔术字符串”以指定属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在写一个服务来收集一个特定类型的对象集合将其原始,字符串和DateTime类型输出到 CSV格式。我有两个以下的语句工作。


$ b

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

vs。 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>排除< TResult>(
表达式< Func< T,TResult>>表达式)

然后我将 expression.Body 传递给 FindMemberExpression 。我已修改 <$ c nhlambdaextensions中的$ c> FindMemberExpression 的ExpressionProcessor.cs 方法项目。我的非常相似的版本的 FindMemberExpression 如下:

  private string FindMemberExpression表达式)
{
if(表达式为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()。等于(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 ?给定我的用例,我在做什么overkill?

解决方案

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

  public IToCsvService< TSource>排除< TResult>(
Expression< Func< TSource,TResult>>表达式)



<这样,你不会得到一个转换表达式,因为不需要转换。例如, p => p.Minute 将由于类型推断而自动以表达式< Func< DateTime,int>>
$ b




这看起来像对我太过分了,因为现在所有你需要的是一个财产 - 至少,这是你的样品显示。 / p>

为什么不开始只是识别某个属性,如果需要可以稍后再展开?



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

  using System; 
使用System.Linq.Expressions;

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

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


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?

解决方案

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.

这篇关于使用lambda表达式来避免使用“魔术字符串”以指定属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆