【发布时间】: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