问题
I need to write an generic extension method for List(T) that conditionally considers each string property of T, then sums a corresponding decimal property of T if a condition is met. My effort thus far:
// foreach(p in Persons) { if(p.Name == "mort"){sum p.Amount;} }
public static double SumIf<T>(this T o, List<T> ListItems,
string targetStr, ?strVals?, ?dblVals?)
{
double sum = 0;
foreach(T item in ListItems)
{
if(item.?strVal? == targetStr){ sum += item.?dblVal? ; }
}
return sum;
}
Thanks for any guidance, mort
回答1:
It sounds like you want a way of extracting the string property and double property (assuming that the "decimal" in your post was a typo rather than the "double" in your code) - Func
is appropriate here:
public static double SumIf<T>(this IEnumerable<T> source,
string targetText,
Func<T, string> textSelector,
Func<T, double> valueSelector)
{
double sum = 0;
foreach (T item in source)
{
if (textSelector(item) == targetText)
{
sum += valueSelector(item);
}
}
return sum;
}
(Note that I've removed the unused initial parameter, and made it an extension method on the list itself. Not using the value feels like a bit of a smell to me... I've also changed the parameter type to IEnumerable<T>
as you don't need it to be a list really.)
Note that this actually mostly equivalent to:
public static double SumIf<T>(this IEnumerable<T> source,
string targetText,
Func<T, string> textSelector,
Func<T, double> valueSelector)
{
return source.Where(x => textSelector(x) == targetText)
.Sum(valueSelector);
}
I'd personally probably go for a general predicate function instead of a string and a text selector:
public static double SumIf<T>(this IEnumerable<T> source,
Func<T, bool> predicate,
Func<T, double> valueSelector)
{
return source.Where(predicate)
.Sum(valueSelector);
}
Then you'd call it with
double sum = list.SumIf(x => x.Name == "mort", x => x.Amount);
... which seems just as good to me as:
double sum = list.SumIf("mort", x => x.Name, x => x.Amount);
... but is considerably more flexible.
As noted in comments, do you really need this at all? Are you using it in sufficient places to make the simple Where/Sum calls unbearable? Heck, you can turn it into just a Sum
call using the conditional operator:
double sum = list.Sum(x => x.Name == "mort" ? x => x.Amount : 0d);
回答2:
You introduce some very specific constraints to your method that make it that it cannot be generic, e.g. T must have a property Amount. It's better to pass those dependencies in as functions:
public static double SumIf<T>(this IList<T> source,
Func<T, bool> pred,
Func<T, double> val)
{
double sum = 0;
foreach (var item in source)
if (pred(item))
sum += val(item);
return sum;
}
Then you can just pass in your predicate and sum property selector as lambdas:
List<Person> people = new List<Person>();
people.Add(new Person() { Name = "Joe", Amount =20.2});
people.Add(new Person() { Name = "Fred", Amount = 11 });
people.Add(new Person() { Name = "Joe", Amount = 5.7 });
double sum = people.SumIf(x => x.Name == "Joe", x => x.Amount);
来源:https://stackoverflow.com/questions/9754502/extension-method-sumif-on-generic-listt