首先,我将描述我想要实现的目标。
我想创建一个方法来抓取属性名称及其值以进行日志记录,所以我有这个:
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&lt; TPropertySource&gt;(Expression&lt; Func&lt; TPropertySource,int&gt;&gt; property,int initialValue,int changedValue)上没有智能感知? 如果我输入 logger.Log&lt; A&gt;(x =&gt; x.Age,1,2); - 它编译得很好,但是intellisense不会启动。
答案 0 :(得分:0)
最简单的方法是使用lambda表达式指定属性源类型:logger.Log((User _) => _.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<SomeType>().Log(_ => _.Age, 1, 2)
。这个API实现和使用显然更复杂,但是当您需要处理多个属性时它可以很方便:
logger.PropertyOf<SomeType>()
.Log(_ => _.Age, 1, 2)
.Log(_ => _.Name, "Mike", "Adam");
当然,您可以结合使用这两种方法,以便API用户可以选择更适合他特定情况的方法。