对List <t> </t>的每个项执行自定义操作

时间:2013-06-23 16:23:35

标签: c# list generics

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

示例:

我有一个如下所示的术语列表,并希望将其“频率”标准化。

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);

(这里是'Term'上的'Frequency',但我应该可以在任何类型的任何属性上执行此操作,属性类型将始终为double)

这就是我精心制作但我无法找到如何执行操作,也无法将结果分配回属性:

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;
}

你将如何实现这一目标?

2 个答案:

答案 0 :(得分: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签名中的此更改,很明显您正在更改现有列表而不会生成新列表。使用旧签名,看起来您将保留旧列表和未触及的对象,但事实并非如此。

答案 1 :(得分:4)

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

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 ...;
     }
 }