【问题标题】:ExpressionTrees: getting a property name and its valueExpressionTrees:获取属性名称及其值
【发布时间】:2013-09-20 15:58:41
【问题描述】:

首先,我将描述我想要实现的目标。

我想创建一个方法来获取属性名称及其值以用于日志记录,所以我有这个:

public void Log<TPropertySource, TProperty>(Expression<Func<TPropertySource, object>> property, TProperty initialValue, TProperty changedValue){...}

现在这需要我指定属性的类型,即 .. meh,因为理论上我可以从表达式中提取它;然而,表达式需要返回一个对象以适应该属性可能具有的所有可能类型。

我正在考虑仅对 BCL 最常用的值类型进行重载,并为其他所有内容使用对象重载,例如

public void Log<TPropertySource>(Expression<Func<TPropertySource, string>> property, string initialValue, string changedValue){...}

public void Log<TPropertySource>(Expression<Func<TPropertySource, int>> property, int initialValue, int changedValue){...}

但这也不理想,因为我最终会遇到十几个重载

所以基本上我想知道是否有更好(更懒惰)的方法来做到这一点?

还有一个问题:为什么我在 Log(Expression> property, int initialValue, int changedValue) 上没有智能感知? 如果我输入 logger.Log(x => x.Age, 1, 2); - 它编译得很好,但智能感知不会启动。

【问题讨论】:

  • 类型推断应该推断两种类型。
  • 我不明白,为什么你不能有public void Log&lt;TPropertySource, TProperty&gt;(Expression&lt;Func&lt;TPropertySource, TProperty&gt;&gt; property, TProperty initialValue, TProperty changedValue)?您说“表达式需要返回一个对象以适应该属性可以具有的所有可能类型”,但是对于 TProperty 可以采用的类型没有限制,因此所有属性类型都应该有效。
  • @SLaks 会很好,但事实并非如此。至少,我无法让它工作。
  • @hvd 我可以,但我必须像 Log(x=>x.Name, "Bob", "Bill");我试图避免必须指定第二个通用参数(在这种情况下为字符串)。
  • @Eugene 更改 API 可以吗? logger.Property&lt;A&gt;(x =&gt; x.Age).Log(1, 2); 应该是可行的。

标签: c# expression-trees


【解决方案1】:

最简单的方法是使用 lambda 表达式指定属性源类型:logger.Log((User _) =&gt; _.Age, 1, 2),以便推断所有类型。

另一种方式是将类型参数分开,这样就可以一个一个地推断或指定。这是一般模式:

假设我们有一些带有 n 个类型参数的方法 M

class C { public void M<T1, T2, ..., Tn>(...) {} }

我们必须推断所有这些或明确指定所有它们。为了独立指定/推断它们,必须更改 API 以为所有类型参数提供中间方法和类型:

class C { public T1Binder<T1> M1<T1>(...) {} } class T1Binder<T1> { public T2Binder<T2> M2<T2>(...) {} } ... class TFinalBinder<T(n - 1)> { public void MFinal<Tn>(...) {} }

现在我们可以有一个调用链,其中每个类型参数都可以独立推断或指定:

c.M1<T1>().M2(t2Value)...MFinal(tnValue);

对于您的情况,它可能如下所示:

public class Logger { public PropertyLogger<TPropertySource> PropertyOf<TPropertySource>() { return new PropertyLogger<TPropertySource>(); } } public class PropertyLogger<TPropertySource> { public void Log<TProperty>( Expression<Func<TPropertySource, object>> property, TProperty initialValue, TProperty changedValue) { } }

可以与例如logger.PropertyOf&lt;SomeType&gt;().Log(_ =&gt; _.Age, 1, 2)。这个 API 的实现和使用显然更复杂,但是当您需要处理超过 1 个属性时,它会很方便:

logger.PropertyOf<SomeType>() .Log(_ => _.Age, 1, 2) .Log(_ => _.Name, "Mike", "Adam");

当然,您可以将这两种方法结合起来,以便 API 用户可以选择更适合其特定情况的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 2016-05-19
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 2012-10-16
    • 2021-12-09
    相关资源
    最近更新 更多