如何将静态方法附加到类而不是类的实例?

4

如果我有一个计算两个整数最大公约数的方法:

public static int GCD(int a, int b)
{
    return b == 0 ? a : GCD(b, a % b);
}

最好的方法是将其附加到System.Math类中?

以下是我想出的三种方法:

public static int GCD(this int a, int b)
{
    return b == 0 ? a : b.GCD(a % b);
}

// Lame...

var gcd = a.GCD(b);

并且:

public static class RationalMath
{
    public static int GCD(int a, int b)
    {
        return b == 0 ? a : GCD(b, a % b);
    }
}

// Lame...

var gcd = RationalMath.GCD(a, b);

并且:

public static int GCD(this Type math, int a, int b)
{
    return b == 0 ? a : typeof(Math).GCD(b, a % b);
}

// Neat?

var gcd = typeof(Math).GCD(a, b);

期望的语法是 Math.GCD,因为这是所有数学函数的标准。

有什么建议吗?我应该怎么做才能得到所需的语法?


你的代码也可以使用 var gcd = typeof(string).GCD(a, b) - 它只需要一个 Type 的实例... 我认为你想做的事情实际上是不可能的。 - Dean Harding
当然,是的。但我希望人们能够理解GCD函数只是另一个数学函数。 - John Gietzen
2
在我看来,最好的做法是在MoreMath类或类似的类中定义GCD。 - jk.
1
@Adam是正确的。这是不可能的。如果没有Math的源代码并重新构建包含它的程序集,您无法向Math类添加静态方法。 - Erv Walter
5个回答

13

不可以。扩展方法仅仅是对调用特定类型的静态函数进行语法糖处理。因此,它们只能操作实例,必须通过传递想要附加的类型的this参数来定义。


2
@John:再次强调,无论是C#的任何版本还是其他任何.NET语言,你所要求的都是不可能实现的。除了“你不能”,没有别的答案可以回答你的问题。 - Adam Robinson
2
@John:我们正在打转。你想要的语法是 System.Math.GCD。我说这是不可能的。你说你知道。我问你为什么提出这个问题。你说是为了获取你知道不可能的语法。??? = 利润。 - Adam Robinson
1
@Nick:是的,扩展方法肯定可以在null引用上操作(或尝试操作)。List<string> foo = null; foo.Select(s => s.Replace("bar", "baz")); - Adam Robinson
1
@Nick:((List<string>)null).Select(...); 再次强调,扩展方法只能作用于实例。直接将方法作为静态方法调用并不是使用它作为扩展方法。再说一遍(这次带点感觉):在这个问题中,我看不出讨论您如何在扩展格式之外调用扩展方法的意义,因为这与此问题完全没有关系 - Adam Robinson
5
@John,你有点困难!你无法扩展System.Math。你已经知道你的选择:在int上使用扩展方法,或者创建自己的静态(任何)Math类。正如Adam所指出的那样,如果你选择My.Math,那么每当需要它时,你就必须完整地限定System.Math。个人而言,我会选择一个名为MathUtility的静态数学实用程序类。 - Anthony Pegram
显示剩余22条评论

6

我更倾向于使用 RationalMath。在这里,你真的不需要扩展方法,因为它们的目的是模拟无法修改对象实例方法。但在这里,应该使用平凡老旧的静态方法。


我会把它命名为<companyName>Math。如果真的想要,你可以编写所有Math函数的包装器,然后告诉所有开发人员只使用<companyName>Math...但这可能有点过度设计了。 - TheSean
1
@TheSean:我也刚把那个作为答案发布了……虽然我同意这可能不值得。 - Tim Goodman

4

鉴于您无法扩展静态的Math类,我会选择样例#2。它遵循了Math使用的模式,不会混乱int方法空间,并且调用简单而清晰。样例#3就太糟糕了 :)


我知道它是这样的。这就是为什么我在这里的原因。 :) - John Gietzen

3
个人而言,我不会按照您想要的方式去做。System.Math只是一个包含一些数学函数的静态类……没有理由它必须包含您想要使用的每个数学函数。
然而,如果您真的需要这样做,我想您可以编写自己的静态Math类,它是System.Math的一种包装器……基本上只需通过将每个函数传递给实际的System.Math类来实现System.Math中的每个函数。就像这样:
public static class Math
{

  public static int GCD(int a, int b)
  {
     return b == 0 ? a : GCD(b, a % b);
  }

  // Implement the System.Math methods
  public static double Pow(double x, double y)
  {
    return System.Math.Pow(x, y);
  }
  // etc.
}

虽然这似乎是一件麻烦的事情,但实际上并没有太多的好处。(有点像反语法糖。)但它可以让你从同一个类中调用Math.GCD(a,b)Math.Pow(x,y),这正是你想要的。


太好了,有真正的建议!我同意这有点过头了。 - John Gietzen

0

好的,我想到了另一种方法:

namespace My
{
    public static class Math
    {
    }
}


namespace MainApp
{
    ...
    var gcd = My.Math.GCD(a, b);
    ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接