1

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

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

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<TPropertySource>(Expression<Func<TPropertySource, int>> property, int initialValue, int changedValue)上没有智能感知?如果我输入logger.Log<A>(x => x.Age, 1, 2); - 它编译得很好,但智能感知不会启动。

4

1 回答 1

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 的实现和使用显然更复杂,但当您需要处理超过 1 个属性时,它会很方便:


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

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

于 2013-09-20T07:58:41.833 回答