简体   繁体   English

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

[英]Execute a custom operation on each item of a List<T>

I would like to do a custom operation on a list's members and be able to specify on which property I would perform it but I'm having a hard time finding the correct syntax for assigning the result back to the property. 我想对列表的成员进行自定义操作,并且能够指定我将执行它的属性,但是我很难找到将结果分配回属性的正确语法。

Example : 示例:

I have a list of terms such as the one below and would like to normalize their 'Frequency'. 我有一个术语列表,如下面的术语,并希望规范化他们的“频率”。

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

Using some syntax like this I should be able to specify the property I am doing the operation on : 使用这样的语法我应该能够指定我正在进行操作的属性:

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

(here it's 'Frequency' on 'Term' but I should be able to do that on any property of any type, property type will always be a double) (这里是'Term'上的'Frequency',但我应该可以在任何类型的任何属性上执行此操作,属性类型将始终为double)

So that's what I crafted but I can't find out how to perform the operation nor assigning the result back to the property : 这就是我精心制作但我无法找到如何执行操作,也没有将结果分配回属性:

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

How would you achieve this ? 你会如何实现这一目标?

If you want to avoid using expressions and reflection, you can simply provide both a getter function and a setter function. 如果要避免使用表达式和反射,可以简单地同时提供getter函数和setter函数。 I also slightly updated the method since it would be altering the objects in original list, so I didn't think it'd be wise to create and return a new list; 我也稍微更新了方法,因为它会改变 原始列表中的对象,所以我认为创建和返回新列表并不明智; the API would be deceptive. API会具有欺骗性。 (you can easily switch it back if desired) (如果需要,你可以轻松切换回来)

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

With usage like: 使用方式如下:

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

And of course it's easy to update to be an extension method so it could be used like: 当然,很容易更新为扩展方法,因此它可以像以下一样使用:

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

With this change in the Normalize signature, it's clear you are altering the existing list and not producing a new one. 通过Normalize签名中的这一更改,很明显您正在更改 现有列表而不是生成新列表。 With the old signature, it would appear you are leaving the old list and objects untouched which is not the case. 使用旧签名,看起来您将保留旧列表和未触及的对象,但事实并非如此。

You can use an Expression to get the handle to the property: 您可以使用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 */);
    //...
}

Furthermore, I would recommend using IEnumerable . 此外,我建议使用IEnumerable This is more flexible and can be converted to lists at any time: 这更灵活,可以随时转换为列表:

 private static IEnumerable<T> Normalize(...)
 {
     foreach(...)
     {
         yield return ...;
     }
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM