2

我想对列表的成员进行自定义操作,并能够指定我将在哪个属性上执行它,但我很难找到将结果分配回属性的正确语法。

例子 :

我有一个术语列表,例如下面的术语,并希望将它们的“频率”标准化。

public class Term
{
    public string Name { get; set; }
    public double Frequency { get; set; }
    public double Weight { get; set; }
}

使用类似这样的语法,我应该能够指定我正在执行操作的属性:

List<Term> list = Normalize(artist.Terms, s => s.Frequency);

(这里是“期限”上的“频率”,但我应该能够对任何类型的任何属性执行此操作,属性类型将始终为双精度)

这就是我精心设计的,但我不知道如何执行操作,也不知道如何将结果分配回属性:

private static List<T> Normalize<T>(List<T> elements, Func<T, double> func)
{
    List<T> list = new List<T>();
    double fMin = elements.Min(func);
    double fMax = elements.Max(func);
    double fDelta = fMax - fMin;
    double fInv = 1.0d / fDelta;
    for (int i = 0; i < elements.Count; i++)
    {
        T t = elements[i];

        // What should I do from here ?
        //double invoke = func.Invoke(term);
        //term.Frequency = (term.Frequency - fMin) * fInv;
     }
    return list;
}

您将如何实现这一目标?

4

2 回答 2

6

如果你想避免使用表达式和反射,你可以简单地提供一个getter函数和一个setter函数。我还稍微更新了这个方法,因为它会改变原始列表中的对象,所以我认为创建并返回一个新列表是不明智的;API 具有欺骗性。(如果需要,您可以轻松地将其切换回来)

private static void Normalize<T>(List<T> elements, Func<T, double> getter, Action<T, double> setter)
{
    double fMin = elements.Min(getter);
    double fMax = elements.Max(getter);
    double fDelta = fMax - fMin;
    double fInv = 1.0d / fDelta;
    for (int i = 0; i < elements.Count; i++)
    {
        T t = elements[i];

        double initialValue = getter(t);
        double newValue = (initialValue - fMin) * fInv;
        setter(t, newValue);
    }
}

使用如下:

Normalize(terms, t => t.Frequency, (t, normalizedFrequency) => t.Frequency = normalizedFrequency);

当然,它很容易更新为扩展方法,因此它可以像这样使用:

terms.Normalize(t => t.Frequency, (t, normalizedFrequency) => t.Frequency = normalizedFrequency);

随着Normalize签名的这种变化,很明显您正在更改现有列表而不是生成新列表使用旧签名,看起来您将保留旧列表和对象不变,但事实并非如此。

于 2013-06-23T16:50:40.107 回答
4

您可以使用 anExpression来获取属性的句柄:

private static List<T> Normalize<T>(List<T> elements, Expression<Func<T, double>> func)
{
    //...
    var expr = (MemberExpression)func.Body;
    var property = expr.Member as PropertyInfo;
    if (property != null)
        property.SetValue(/* element */, /* new value */);
    //...
}

此外,我建议使用IEnumerable. 这样更灵活,可以随时转换为列表:

 private static IEnumerable<T> Normalize(...)
 {
     foreach(...)
     {
         yield return ...;
     }
 }
于 2013-06-23T16:31:53.433 回答