【问题标题】:Using an expression tree to read the name and value of a property. Is there an alternative?使用表达式树来读取属性的名称和值。有替代方案吗?
【发布时间】:2009-12-04 17:45:47
【问题描述】:

假设

假设我有一个具有属性的类:

class ClassWithProperty
{
    public string Prop { get; private set; }

    public ClassWithProperty(string prop)
    {
        this.Prop = prop;
    }
}

现在假设我已经创建了该类的一个实例:

var test = new ClassWithProperty("test value");

我想要什么

我希望将其打印到控制台:

Prop = 'test value'

小菜一碟!我使用这段代码来产生想要的输出:

Console.WriteLine("Prop = '{1}'", test.Prop);

问题

我违反了 DRY,因为“Prop”在上面的代码中出现了两次。如果我重构类并更改属性的名称,我还必须更改字符串文字。如果我有一个具有许多属性的类,那么还有很多样板代码。

建议的解决方案

string result = buildString(() => c.Prop);
Console.WriteLine(result);

buildString 方法如下所示:

private static string buildString(Expression<Func<string>> expression)
{
    MemberExpression memberExpression = (MemberExpression)expression.Body;
    string propertyName = memberExpression.Member.Name;

    Func<string> compiledFunction = expression.Compile();
    string propertyValue = compiledFunction.Invoke();

    return string.Format("{0} = '{1}'", propertyName, propertyValue);
}

问题

上述解决方案运行良好,我对此很满意,但如果有一些更简单且不那么“可怕”的方法来解决这个问题,那会让我更开心。是否有一些更简单的替代方法可以用更少和更简单的代码实现相同的结果?也许没有表达式树的东西?

编辑

基于 Manu 的好主意(见下文),我可以使用这种扩展方法:

static public IEnumerable<string> ListProperties<T>(this T instance)
{
    return instance.GetType().GetProperties()
        .Select(p => string.Format("{0} = '{1}'",
        p.Name, p.GetValue(instance, null)));
}

这对于获取实例的所有属性的字符串表示非常有用。

但是:从这个枚举中,我如何才能安全地选择类型安全的特定属性?我会再次使用表达式树......还是我没有看到树木的木材?

编辑 2

在这里使用反射或表达式树是个人喜好问题。

Luke 使用投影初始化器的想法非常棒。根据他的回答,我终于构建了这个扩展方法(基本上只是他回答的 LINQ 版本):

public static IEnumerable<string> BuildString(this object source)
{
    return from p in source.GetType().GetProperties()
           select string.Format("{0} = '{1}'", p.Name, p.GetValue(source, null));
}

现在调用将如下所示:

new { c.Prop }.BuildString().First()

这看起来比我原来的 lambda 调用要好一些(但我猜这也是一个口味问题)。然而,Luke 的建议优于我的解决方案,因为它允许指定任意数量的属性(见下文)。

【问题讨论】:

    标签: c# expression-trees


    【解决方案1】:

    您可以将匿名类型传递给BuildStrings 方法,利用投影初始化程序自动创建匿名类型属性的名称和值。在方法内部,您将使用反射来询问这些属性。

    如果您愿意,这还允许您传递多个项目。您还可以传递字段和局部变量以及属性,因为它们都将被投影为匿名类型的属性,然后可以使用GetProperties 等进行询问。(当然,该方法实际上所做的只是枚举所有传入的对象的属性。你可以传递任何类型。)

    string result = BuildStrings(new { test.Prop }).First();
    Console.WriteLine(result);
    
    // or
    
    string foo = "Test";
    int bar = 42;
    
    string results = BuildStrings(new { foo, bar, test.Prop });
    foreach (string r in results)
    {
        Console.WriteLine(r);
    }
    
    // ...
    
    public static IEnumerable<string> BuildStrings(object source)
    {
        return source.GetType().GetProperties().Select(
            p => string.Format("{0} = '{1}'", p.Name, p.GetValue(source, null)));
    }
    

    【讨论】:

    • 这是表达式树的替代方案,可以选择特定的属性。我喜欢...
    • 这很酷。如果您想提高性能,您可以将类型缓存到静态变量并针对该变量执行选择。
    【解决方案2】:

    我其实很喜欢你最初使用表达式树的想法。这是 ET 的一个很好的用例。但是你让它看起来有点吓人,因为你编译并执行一个表达式树来获取属性的值,而你所需要的只是名称。让方法只返回名称,然后像在第一次“非 DRY”尝试中那样使用它。

        private static string buildString(Expression<Func<string>> expression) 
        { 
            MemberExpression memberExpression = (MemberExpression)expression.Body; 
            return memberExpression.Member.Name; 
        }
    

    然后

            Console.WriteLine("{0} = {1}", buildString(() => c.Prop), c.Prop);
    

    看起来没那么可怕。是的,您在这里使用了两次 c.Prop,但我认为您获得了显着的性能提升,因为您不需要表达式树编译。而且您仍然不使用任何字符串文字。

    【讨论】:

      【解决方案3】:
      var listOfPropertyNamesAndValues = this.GetType().GetProperties()
          .Select(prop => string.Format("{0} = '{1}'", 
          prop.Name, prop.GetValue(this,null)));
      

      如果您想获取特定的属性,您必须将其名称作为字符串传递并通过反射获取(只需在上述查询中添加 where 子句)。好消息是您不再违反 DRY,坏消息是它不是类型安全的。

      【讨论】:

      • typeof(this) eeeeeeeeeeeeeeeeeeeekkkkkkkkkk!! ;p
      • @Manu:感谢您的解决方案。它非常有用。我已经将您的建议纳入问题中,但我遇到了一些小问题。
      【解决方案4】:

      【讨论】:

      • 是的,这是另一种选择,但我认为代码不会比使用表达式树简单。如果我错了,请发布示例代码。
      • 我不会不同意,我想你是否更喜欢反射而不是表达式树是个人喜好。
      • Aaahh ... Manu 有一个例子。是的,那是品味问题。 ;-)
      【解决方案5】:

      我已经看到它使用a delegate and IL interrogation 完成了,但这并不简单。简而言之,没有:C# 中没有infoof。以下是 Eric Lippert 对此的看法:In Foof We Trust: A Dialogue

      【讨论】:

      • 刚刚用谷歌搜索了一下,还浏览了那个链接(很难理解,猜想必须读两遍;-))。这个概念从何而来? C、Smalltalk?不得不承认我从来没有听说过。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      相关资源
      最近更新 更多