将一个IEnumerable的数值作为参数传递给方法

本文关键字:参数传递 方法 IEnumerable 一个 | 更新日期: 2023-09-27 18:09:27

我正在使用一个非常简单的程序来获取一个双精度数组并返回标准差。这部分工作,但我想使代码更可重用。我想这样做,以便该方法可以接受任何类型的参数,可以被认为是数字,并返回标准偏差,而不是硬编码双类型(就像我最初在这个程序中所做的那样)。人们该如何处理这个问题,恰当的术语是什么?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

    namespace ConsoleApplication5
    {
        class Program
        {
            static void Main(string[] args)
            {
                double[] avg = { 3.4, 55.6, 10.0, 4.5, 2, 2 };
                double x = avg.Average();
                //first round of testing
                Console.WriteLine("The average of the first array is below ");
                Console.WriteLine(x);
                Console.WriteLine("below should be the standard deviation!");
                Console.WriteLine(CalculateStandardDeviation(avg));
                Console.ReadLine();
                int[] intAvg = { 4, 3, 5, 6, 2 };
                double secondAvg = intAvg.Average();
                Console.WriteLine("The average of the second array is below ");
                Console.WriteLine(secondAvg);
                //this is where the error is happening
                //CalculateStandardDeviation(secondAvg);
            }
            //this is where I tried to make the query more reusable
            public static double CalculateStandardDeviation(IEnumerable<double> values)
            {
                double avg = values.Average();
                double sum = 0;
                foreach (double d in values)
                {
                    sum += Math.Pow((d - avg), 2);
                }
                return Math.Pow(sum / (values.Count() - 1),.5);
            }
        }
    }

将一个IEnumerable的数值作为参数传递给方法

你可以这样写:

public static decimal CalculateStandardDeviation<T>(IEnumerable<T> values)
{
    IEnumerable<decimal> decimalValues = values.Select(v => Convert.ToDecimal(v));
    decimal result = 0;
    // calculate standard deviation on decimalValues
    return result;
}

如果values包含不能转换为小数的值,它将抛出异常,但如果值是适当的类型,它将工作,我认为这是完全有意义的。

不幸的是,没有适合所有数字的基类。您可以使用通用的运行时检查方法或编译时安全的重载集来完成此操作。

通用方法:

public static T CalculateStandardDeviation(IEnumerable<T> values)
{
    var valueArray = values.Select(Convert.ToDecimal).ToArray();
    //...
    return (T)standardDeviation;
}

使用单一泛型方法的问题是,您不能对类型参数施加类型约束,从而将其限制为仅为数字类型。你将不得不求助于运行时的失败。没有什么可以阻止你用字符串、对象、颜色或httpwebrequest等数组调用方法,除非你知道如何计算颜色的标准偏差,否则你应该坚持为特定的数字类型单独覆盖:

我建议使用decimal类型作为您的主要实现,然后将所有内容都转换为它。

特定类型过载:

public static decimal CalculateStandardDeviation(IEnumerable<decimal> values)
{
    //...
}
public static double CalculateStandardDeviation(IEnumerable<double> values)
{
    return (double)CalculateStandardDeviation(values.Select(Convert.ToDecimal));
}
public static int CalculateStandardDeviation(IEnumerable<int> values)
{
    return (int)CalculateStandardDeviation(values.Select(Convert.ToDecimal));
}
// etc...

使用c#泛型。

你的函数签名将是:

public static T CalculateStandardDeviation(IEnumerable<T> values)

你可以这样使用:

int stdDev = CalculateStandardDeviation([int-array]);
double stdDev = CalculateStandardDeviation([double-array]);

请点击链接:

http://msdn.microsoft.com/en-us/library/ms379564%28VS.80%29.aspx

编辑:

要解决泛型类型的Average问题,请查看此库:

如何实现对不同值类型进行数学计算的泛型方法

Obs:来自Brian的建议

EDIT你应该使用JLRishe的答案,它比这个要优雅得多。

你可能应该首先在你的方法中添加泛型,然后使用类型转换器将未知的输入转换为双精度类型,如下所示:

public static double CalculateStandardDeviation<TSource>(IEnumerable<TSource> inputs)
{
    var converter = TypeDescriptor.GetConverter(typeof (double));
    if (!converter.CanConvertFrom(typeof(TSource)))
        return 0;
    var values = new List<double>();
    foreach (var value in inputs)
    {
        values.Add((double) converter.ConvertFrom(value));
    }
    // Your logic here ...
    return ...;
}

我没有测试这个代码片段,但是您应该明白了。

前言:这个答案建立在如何验证类型是否重载/支持某个操作符?和http://www.codeproject.com/Articles/87438/TinyLisp-A-Language-and-Parser-to-See-LINQ-Express第二个链接显示了如何编译和求值linq表达式。

简而言之,您可以放弃静态类型安全性,并在运行时检查类型是否支持特定操作(第一个链接),如果不支持,您可以抛出异常,如下面的示例所示:

void Main()
{
    DoAdd<float>(5,6);
    DoAdd<int>(5,6);
    DoAdd<bool>(true,false);
}

// Define other methods and classes here
static void DoAdd<T>(T in1, T in2){
if(!HasAdd<T>()){throw new Exception("Unsupported Type!");}
 var c1 = Expression.Constant(in1, typeof(T));
 var c2 = Expression.Constant(in2, typeof(T));
 var expression=Expression.Add(c1, c2);
 Expression<Func<T>> lExpression = Expression.Lambda<Func<T>>(expression);
 Func<T> fExpression = lExpression.Compile();
 Console.WriteLine(fExpression());
}

static bool HasAdd<T>() {
    var c = Expression.Constant(default(T), typeof(T));
    try {
        Expression.Add(c, c); // Throws an exception if + is not defined
        return true;
    } catch {
        return false;
    }
}

c# 6.0将支持将IEnumerable的数值作为参数传递给方法