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

时间:2013-04-16 19:19:17

标签: c# visual-studio-2010

我正在玩一个非常简单的程序来获取一系列双打并返回标准偏差。这部分工作但我想让代码更可重用。我想这样做,所以方法可以接受任何类型的参数,可以被认为是数字并返回标准偏差而不是硬编码双重类型(就像我最初在这个程序中做的那样)。如何解决这个问题以及适当的术语是什么?

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

            }
        }
    }

6 个答案:

答案 0 :(得分:6)

您可以使用以下内容:

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包含无法转换为小数的值,它将抛出异常,但如果值的类型合适,它将起作用,我认为这非常有意义。

答案 1 :(得分:5)

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

通用方法:

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

    //...

    return (T)standardDeviation;
}

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

我建议使用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...

答案 2 :(得分:2)

使用C#Generics。

您的功能签名将是:

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

编辑:

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

How to Implement Generic Method to do Math calculations on different value types

Obs:Brian的建议。

答案 3 :(得分:0)

修改 你应该使用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 ...;
}

我没有测试过这个片段,但你明白了。

答案 4 :(得分:0)

前言: 这个答案建立在 How to verify whether a type overloads/supports a certain operator?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;
    }
}

答案 5 :(得分:0)

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