在我的代码库中,我有大量的类,需要对其成员执行各种集体操作才能获得结果(平均值,标准偏差,置信区间等)。

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


用于以下代码的近乎重复的替换:

    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关键字,我无法通过这些委托调用StandardDeviationthis。从StandardDeviation实现中删除该代码并将调用替换为以下内容后:

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


它仍然不适合委托,会产生以下错误消息:

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


我有什么办法为上述通用静态LINQ扩展创建委托?这些方法将以独特的方式在各种类中使用,因此它们必须是通用的。

最佳答案

这使我想起了MapReduce。您具有要映射(例如,选择对象的单个属性)到数字值的对象的集合,然后要将此新的数字值的集合减少为单个数字的值。您可以定义一个包装方法(为了更容易使用):

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


定义Operation -Method如下:

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

10-05 22:29