Метод расширения, SumIf в общем списке <T> - PullRequest
2 голосов
/ 18 марта 2012

Мне нужно написать общий метод расширения для List (T), который условно рассматривает каждое строковое свойство T, а затем суммирует соответствующее десятичное свойство T, если выполняется условие.Мои усилия на данный момент:

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

Спасибо за любое руководство, морт

Ответы [ 2 ]

4 голосов
/ 18 марта 2012

Звучит так, будто вам нужен способ извлечения строкового свойства и свойства double (при условии, что «десятичное» в вашем посте было опечаткой, а не «двойным» в вашем коде) - Func здесь уместно:

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

(Обратите внимание, что я удалил неиспользуемый начальный параметр и сделал его методом расширения в самом списке. Неиспользование значения кажется мне немного неприятным запахом ... Я также изменил тип параметра на IEnumerable<T>, так как вам не нужно, чтобы это был действительно список.)

Обратите внимание, что на самом деле это в основном эквивалентно:

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

Лично я бы, вероятно, использовал обычную функцию предиката вместо строки и текстового селектора:

public static double SumIf<T>(this IEnumerable<T> source, 
          Func<T, bool> predicate,
          Func<T, double> valueSelector)
{
    return source.Where(predicate)
                 .Sum(valueSelector);
}

Тогда вы бы позвонили с

double sum = list.SumIf(x => x.Name == "mort", x => x.Amount);

... что мне так же хорошо, как:

double sum = list.SumIf("mort", x => x.Name, x => x.Amount);

... но значительно более гибок.

Как отмечено в комментариях, вам это вообще нужно? Вы используете его в достаточном количестве мест, чтобы сделать простые вызовы Where / Sum невыносимыми? Черт возьми, вы можете превратить его в Sum вызов, используя условный оператор:

double sum = list.Sum(x => x.Name == "mort" ? x => x.Amount : 0d);
0 голосов
/ 18 марта 2012

Вы вводите некоторые очень специфические ограничения для вашего метода, которые делают его невозможным для общего использования, например, Т должен иметь свойство Количество. Эти зависимости лучше передать в виде функций:

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

Тогда вы можете просто передать свой предикат и суммировать селектор свойств как лямбды:

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...