我想对列表的成员进行自定义操作,并且能够指定我将执行哪个属性,但是我很难找到将结果分配回属性的正确语法。
示例:
我有一个如下所示的术语列表,并希望将其“频率”标准化。
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;
}
你将如何实现这一目标?
答案 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 ...;
}
}