将给定的 属性-access 转换为链式字符串

Convert a given property-access to chained string

我正在尝试编写一个扩展方法来从给定的 LambdaExpression.

中检索 属性 路径

我希望可以这样称呼它

var path = ((Event e) => e.Address.City.Name).ToChainedPath();

这将 return Address.City.Name 作为 string. 分隔。

目前我的扩展是这样的

public static string ToChainedPath<TSource, TProperty>(this Expression<Func<TSource, TProperty>> expr, char separator = '.')
{
    MemberExpression me;

    switch (expr.Body.NodeType)
    {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expr.Body as UnaryExpression;
            me = ue?.Operand as MemberExpression;
            break;
        default:
            me = expr.Body as MemberExpression;
            break;
    }

    var propertyNames = new List<string>();

    while (me != null)
    {
        propertyNames.Add(me.Member.Name);
        me = me.Expression as MemberExpression;
    }

    propertyNames.Reverse();

    return string.Join(separator, propertyNames);
}

这样使用时效果很好
Expression<Func<Event, string>> expression = e => e.Address.City.Name;
var propertyChain = expression.ToChainedPath();

但这对我想要的不起作用,因为 (Event e) => e.Address.City.NameFunc<Event, string> 而不是 Expression<Func<Event,string>>。 我已经尝试将 Func 转换为 Expression,但到目前为止没有任何帮助。

我还更改了扩展名以扩展 Func 而不是 Expression 但后来我松开了 Body.

这甚至可以做到吗?

lambda 表达式本身没有类型(参见 spec),因此编译器不知道在哪里可以找到 ToChainedPath 如果您只是这样做:

((Event e) => e.Address.City.Name).ToChainedPath();

您还没有给它任何关于 lambda 表达式应该转换成什么的类型信息(委托类型?表达式类型?)。因此,这种确切的语法是不可能的。

可以提供类型信息的另一种方式是使用方法参数。

EnclosingClass.GetChainedPath(e => e.Address.City.Name);

如果编译器可以解析 GetChainedPath 是什么,它就会知道它有一个类型为 Expression<Func<Event, string>>:

的参数
public static string GetChainedPath<TSource, TProperty>(Expression<Func<TSource, TProperty>> expr, char separator = '.')

所以它知道 lambda 应该转换成什么类型​​。

如果你想让这个更加简洁(省略EnclosingClass.),你可以为GetChainedPath做一个using static

using static YourNamespace.EnclosingClass;