【问题标题】:How can I combine two functions into a single one using generics?如何使用泛型将两个函数组合成一个函数?
【发布时间】:2018-07-29 04:36:34
【问题描述】:

假设我有以下功能:

public int Compute(int a, int b, int c)
{   
    return (a + b +c)/3;
}

public double Compute(double a, double b, double c)
{
    return ((a + b + c) / 3.0) / 209;
}

我希望区别很明显。双精度值需要除以 209(一个常数值),而整数则不需要。

使用泛型将这两个函数组合成一个函数的最佳方法是什么?

【问题讨论】:

  • 泛型用于进行操作……嗯……泛型。 IOW,它们用于使操作不关心它们正在操作的类型。您的操作显然确实关心,因为它的行为不同。
  • 我认为最好的方法是完全不这样做并保持原样,是否有理由需要通用版本?
  • 这样写代码会更干净...
  • 签名可以是通用的:public T Compute(T a, T b, T c)。但是由于你用双精度数和整数做不同的操作,你必须检查泛型方法中的类型,这意味着它首先不应该是泛型的。此外,并非所有类型都支持数学运算,因此在大多数非数字类型上都会失败。
  • 即使将带有(a+b+c)/3 的部分提取到共享泛型方法中也不可行,因为C# 无法将泛型类型参数限制为允许+ 之类的“算术”参数和/。如果这是你真的想问的,那是不可能的。

标签: c# generics


【解决方案1】:

我不确定这是否有意义。 泛型是避免为不同对象类型编写相似代码的方法。

但在您的情况下,我没有看到任何可以概括的类似代码,因此保持功能不同可以更好地解决任务。

【讨论】:

  • 是的,但是如何将它推广到其他类型,例如 long?有什么规定吗?
  • 没有。只区分整数和实数类型。
  • 那我之前写的就没意思了。你只会让逻辑更难理解。
  • @anonymous 非数字结构呢?
  • @RufusL,他们被排除在外。
【解决方案2】:

简答

你不能把它变成一个函数。

长答案

你唯一的通用代码是这样的:

return (a + b +c)/

您最多可以使用泛型并做到这一点(C# 不可能):

public static T Compute<T>(T a, T b, T c, T divisorSmall, int divisor) 
{
    return ((a + b + c) / divisorSmall) / divisor;
    // Results in compiler error: Error CS0019  Operator '+' cannot be 
    // applied to operands of type 'T' and 'T'  
}

并像这样使用它:

Compute(1, 2, 3, 3, 1); // For integers
Compute(1.0, 2.0, 6.0, 3.0, 209); // For doubles

但你不能这样做,因为you cannot restrict the type T to support arithmetic operationrestrict T to be numeric

此外,即使有可能,在这种特定情况下您也不会获得太多收益,因为看看我假设的解决方案中的用法看起来多么笨拙。

【讨论】:

    【解决方案3】:

    您不应该使用泛型,但您可以测试abc 是否为ints,然后选择您的操作:

    private double Compute(double a, double b, double c)
    {
        /*         check if a, b and c are integers         int if true          double if false    */
        return (a % 1 == 0 && b % 1 == 0 && c % 1 == 0) ? (a + b + c) / 3 : ((a + b + c) / 3.0) / 209;
    }
    
    [TestMethod()]
    public void Int()
    {
        int a = 1;
        int b = 2;
        int c = 3;
        int result = (int)Compute(a, b, c);
    
        int expected = (1 + 2 + 3) / 3;
    
        Assert.AreEqual(expected, result);
    }
    
    [TestMethod()]
    public void Double()
    {
        double a = 1.1;
        double b = 2.2;
        double c = 3.3;
        double result = Compute(a, b, c);
    
        double expected = ((1.1 + 2.2 + 3.3) / 3.0) / 209;
    
        Assert.AreEqual(expected, result);
    }
    

    两个测试都通过了

    【讨论】:

    • 这样做毫无意义:为什么有人会将int 传递给签名期望double 的方法?当然这是可行的,但这更像是黑客攻击。作为一名优秀的程序员,您需要针对接口(签名)进行编程,而不是针对方法的实现。
    【解决方案4】:

    我有个主意。我可以创建一个泛型方法,它为每个案例intdouble 接收一个Delegate。这个版本的小提琴作品https://dotnetfiddle.net/Q15bYK

        public static void Main()
        {   
            Func<double,double,double,double> x = (d1,d2,d3) => {return ((d1 +d2 + d3)/ 3.0) / 209;};
    
            Func<int,int,int,int> y = (i1,i2,i3) => {return (i1 + i2 + i3)/ 3;};
    
            var rsDouble = Compute<double>(1.0,2.0,3.0,x);
            Console.WriteLine(rsDouble);
    
            var rsInt = Compute<int>(1,2,3,y);
            Console.WriteLine(rsInt);
    
        }
    
        public static T Compute<T>(T a, T b, T c, Func<T,T,T,T> action)
        {
            return action(a,b,c);
        }
    

    但这似乎我的回答使情况变得复杂,我同意其他回答,泛型用于为不同的对象类型编写类似的代码,而不是为每个参数编写不同的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多