2015-01-23 4 views
4

В моем коде, у меня есть большое количество классов, которые нужно выполнять различные коллективные операции на своих членов, чтобы получить результаты (среднее, стандартное отклонение, доверительные интервалы и т.д.)Делегат для LINQ расширений

Average LINQ расширения метод уже существует, но StandardDeviation не делает, поэтому я реализовал это следующим образом:

public static double StandardDeviation<T>(this IEnumerable<T> source, Func<T, double> selector) 
{ 
    var average = source.Average(selector); 
    var sumOfSquares = source.Sum(sample => Math.Pow(selector(sample) - average, 2)); 
    return Math.Pow(sumOfSquares, 0.5); 
} 

Я хотел бы найти способ определения делегата для этой функции, в идеале в качестве расширения LINQ, чтобы избежать дублирования кода. Ниже приведен пример текущего использования двух методов:

public override void Average(IList<ThisType> samples) 
    { 
     TotalEntered = samples.Average(sample => sample.TotalEntered); 
     TotalExited = samples.Average(sample => sample.TotalExited); 
     MinimumContents = samples.Average(sample => sample.MinimumContents); 
     AverageContents = samples.Average(sample => sample.AverageContents); 
     MaximumContents = samples.Average(sample => sample.MaximumContents); 
     MinimumTime = samples.Average(sample => sample.MinimumTime); 
     AverageTime = samples.Average(sample => sample.AverageTime); 
     MaximumTime = samples.Average(sample => sample.MaximumTime); 
     StdDevTime = samples.Average(sample => sample.StdDevTime); 
     AverageNonZeroTime = samples.Average(sample => sample.AverageNonZeroTime); 
     PercentageWithinLimit = samples.Average(sample => sample.PercentageWithinLimit); 
     base.Average(samples); 
    } 

    public override void StandardDeviation(IList<ThisType> samples) 
    { 
     TotalEntered = samples.StandardDeviation(sample => sample.TotalEntered); 
     TotalExited = samples.StandardDeviation(sample => sample.TotalExited); 
     MinimumContents = samples.StandardDeviation(sample => sample.MinimumContents); 
     AverageContents = samples.StandardDeviation(sample => sample.AverageContents); 
     MaximumContents = samples.StandardDeviation(sample => sample.MaximumContents); 
     MinimumTime = samples.StandardDeviation(sample => sample.MinimumTime); 
     AverageTime = samples.StandardDeviation(sample => sample.AverageTime); 
     MaximumTime = samples.StandardDeviation(sample => sample.MaximumTime); 
     StdDevTime = samples.StandardDeviation(sample => sample.StdDevTime); 
     AverageNonZeroTime = samples.StandardDeviation(sample => sample.AverageNonZeroTime); 
     PercentageWithinLimit = queueSamples.StandardDeviation(sample => sample.PercentageWithinLimit); 
     base.StandardDeviation(samples); 
    } 

Я попытался создать делегат для этих методов, а именно:

public delegate double CollectiveOperation<T>(IEnumerable<T> source, Func<T, double> selector); 

Для использования в следующей замене для выше вблизи -duplication кода:

public void Operation(IList<ThisType> samples, LINQExtensions.CollectiveOperation<ThisType> Operation) 
    { 
     BufferMinimumTime = samples.Operation(sample => sample.BufferMinimumTime); 
     BufferAverageTime = samples.Operation(sample => sample.BufferAverageTime); 
     BufferMaximumTime = samples.Operation(sample => sample.BufferMaximumTime); 
     BufferStdDevTime = samples.Operation(sample => sample.BufferStdDevTime); 
     TotalMinimumTime = samples.Operation(sample => sample.TotalMinimumTime); 
     TotalAverageTime = samples.Operation(sample => sample.TotalAverageTime); 
     TotalMaximumTime = samples.Operation(sample => sample.TotalMaximumTime); 
     TotalStdDevTime = samples.Operation(sample => sample.TotalStdDevTime); 
     base.Operation(samples); 
    } 

Однако, я не был в состоянии назвать Average или StandardDeviation через эти делегаты, предположительно из-за this ключевое слово. После удаления этого из StandardDeviation реализации и замена вызовов с чем-то вроде:

BufferMinimumTime = Operation(samples, sample => sample.BufferMinimumTime); 

Он по-прежнему не будет соответствовать с делегатом, производя следующее сообщение об ошибке:

Expected a method with 'double StandardDeviation(IEnumerable<ThisType>, Func<ThisType, double>)' signature. 

Есть ли способ для мне создать делегат для общего статического расширения LINQ, как указано выше? Эти методы будут использоваться в различных классах уникальными способами, поэтому они должны быть общими.

+0

Мне удалось заставить это работать с вашими определениями 'CollectiveOperation ' и 'StandardDeviation ', Stock 'Enumerable.Average ' и небольшой тестовый класс для 'T'. Можете ли вы сконденсировать свой вопрос на примере с минимальным количеством кода, необходимым для воспроизведения проблемы? – Rawling

+0

сначала ваше расширение StandardDeviation должно использовать возврат доходности, а не полный возврат коллекции, посмотрите на это в Интернете. Во-вторых, вы можете использовать отражение и получить список членов в образце, где имя соответствует локальному члену, а затем вызвать средний метод, присваивающий результат локальному члену. – Franck

+0

@Franck Как может 'StandardDeviation' использовать' yield return', когда ему нужно только вернуть одно значение, а не последовательность? – Rawling

ответ

0

Это напоминает мне немного MapReduce. У вас есть набор объектов, которые вы хотите сопоставить (например, выбрать одно свойство объекта) с числовым значением, после чего вы хотите уменьшить эту новую коллекцию числовых значений до единственного числового значения. Вы можете определить Wrapper-метод (для более легкого использования):

public static TResult MapReduce<T, TMap, TResult>(this IEnumerable<T> source, Func<T, TMap> mapper, Func<IEnumerable<TMap>, TResult> reducer) 
    { 
     return reducer(source.Select(mapper)); 
    } 

Define Operation -метода следующим образом:

public void Operation(IEnumerable<TestClass> samples, Func<IEnumerable<double>, double> operation) 
    { 
     var foo = samples.MapReduce(s => s.Foo, operation); 
     var bar = samples.MapReduce(s => s.Bar, operation); 
    } 

И называть его:

 this.Operation(new List<TestClass>(), Enumerable.Average); 
     this.Operation(new List<TestClass>(), MyLinqExtensions.StandardDeviation); 

Но вы должны Внесите свой StandardDeviation немного другой:

internal static double StandardDeviation(this IEnumerable<double> source) 
    { 
     var average = source.Average(); 
     var sumOfSquares = source.Sum(sample => Math.Pow(sample - average, 2)); 
     return Math.Pow(sumOfSquares, 0.5); 
    } 
Смежные вопросы