当前位置: 首页 > 知识库问答 >
问题:

为什么静态和非静态方法不能共享相同的签名?

艾宏远
2023-03-14

C#提供了函数重载时使用的以下签名特性。

我们知道,对于重载,只考虑参数;它们的数量和类型,但多态性的目标是根据调用策略提供相同的名称,但不同的用法。

如果我有一个类包含两个具有相同名称和签名的方法,而一个是静态的,另一个不是,C#编译器抛出一个错误;"类已经定义了一个名为'foo'的成员,具有相同的参数类型"。对这两个方法的调用将是不同的;一个带有对象名,一个带有类名的静态方法。因此,调用策略没有含糊之处。那为什么它会抛出一个错误?

 class Example {

    public void foo() { }
    public static void foo() { }

}

class Program
{
    static void Main(string[] args)
    {

        Example e = new Example();
        e.foo(); 

    }
}

共有3个答案

康照
2023-03-14

我觉得你的问题是“为什么标准选择禁止声明两种仅因静态关键字而不同的方法?”,因此“因为标准这么说”的答案对我来说并不合适。

现在,问题是,可能有任何原因。标准就是法律,它可以是任意的。如果没有参与语言设计的人的帮助,我们所能做的就是推测原因,试图揭示法律的精神。

这是我的猜测。我认为这一选择有三个主要原因:

C和Java是C#的鼓舞人心的语言,遵守与这些语言相同的重载规则是有意义的。至于为什么在这些语言中是这样,我不知道。关于C,我发现了一个类似的问题,尽管没有回答为什么会这样(在“标准这么说”之外)。

正如其他人和OP所指出的,允许除了static关键字之外的相同签名会强制用户以明确的方式调用方法(通过在类名或实例名前加前缀)。这增加了代码的复杂性。当然,这可以通过字段和参数来完成。但是,有些人不同意这种用法,他们更喜欢为字段选择不同的名称(在字段前面加上u或m_u)。

这是我在这里的理解,所以我可能完全错了(至少@user2864740认为这个参数是可疑的——请参阅注释),但我觉得静态成员是在OOP中引入“函数式编程”的一种方式。它们不绑定到特定的实例,因此它们不会修改对象的内部状态(如果它们修改另一个对象的状态,那么它们应该是该另一个对象的非静态方法),以一种“纯”的方式。因此,我不理解“纯函数”在语义上如何与常规对象方法足够接近,从而使它们共享相同的名称。

澹台衡
2023-03-14

发生此错误是因为这是C语言规范中定义行为的方式。任何“模棱两可”的用法(或消除歧义的方法)都是不相关的,尽管这种推理和边缘案例可能导致设计师不明确允许这种区别。。或者,它可能只是一种对基础数据的C#编目。NET CLI/CLR限制1

从C#规范中的3.6签名和重载(与链接留档一致),格式化为项目符号:

方法的签名包括

  • 方法的名称,
  • 类型参数的数量,以及
  • 每个形式参数的类型和种类(值、引用或输出)...

方法修饰符(包括static)在此不被视为方法签名的一部分。

从“1.6.6方法”中,我们得到了限制和一致的总结:

方法的签名在声明该方法的类中必须是唯一的。方法的签名由方法的名称、类型参数的数量和{其参数的数量、修饰符和类型}组成。。

此限制适用于多态性所考虑的方法之前(且独立于该方法)。

另外,作为结束语:实例方法必须是虚拟的或通过接口访问,才能在C#中运行时多态。(方法隐藏和方法重载可以说是编译时多态性的一种形式,但这是另一个主题。)

1有人支持这一点,这仅仅是由于限制了。NET CLI/CLR本身不值得绕过(即出于互操作性原因)。来自ECMA-335中的“I.8.6.1.5方法签名”:

方法签名由

  • 呼叫约定[CLS规则15:“CLS支持的唯一呼叫约定是标准托管呼叫约定],
  • 泛型参数的数量,如果方法是泛型的,
  • [省略规则]
  • 零个或多个参数签名的列表,每个参数对应一个方法-和,
  • 结果值的类型签名(如果生成)

方法签名由方法定义声明。除了参数签名的约束外,只能向方法签名添加一个约束[CLS规则15:“vararg约束不是CLS的一部分”]:

  • 可以包括vararg约束,以指示超过此点的所有参数都是可选的。出现时,调用约定应为支持变量参数列表的约定

因此,C#/CLS和ECMA签名组件之间的交集是方法名、“通用参数的数量”和“零个或多个参数签名的列表”。

柯星辰
2023-03-14

它抛出错误的原因是,可以从非静态方法调用静态方法,而无需指定类型名。在这种情况下,编译器将无法确定调用哪个方法。

public class Foo()
{
   public static void MyMethod() {};
   public void MyMethod() {}

   public void SomeOtherMethod()
   {
      MyMethod(); // which method we're calling static or non-static ?
   }
}

编辑

刚找到这篇关于你的案子的帖子。你可能也想检查一下。

 类似资料:
  • 问题内容: 请参见下面的代码段: 代码1 代码2 这些代码段之间有什么区别?两者都15作为答案输出。 问题答案: 静态方法属于类本身,而非静态(aka实例)方法属于从该类生成的每个对象。如果你的方法执行的操作不依赖于其类的单个特征,请将其设置为静态(这将使程序的占用空间减小)。否则,它应该是非静态的。 例: 你可以像这样调用静态方法:。如果你使用method2尝试该操作,它将失败。但这将起作用:;

  • 我真的对此感到困惑!我有2个班,俱乐部和会员。在Membership中,我有方法getMonth();在Club中,我有joinedMonth(),它接受参数'month'--所以用户输入一个月,然后我希望它返回在该特定月份加入的成员资格。 我试图从Club类调用getMonth()方法,这样我就可以继续比较月份的整数。但是,当我试图调用该方法时,我只得到提到的“非静态方法getMonth()不能

  • 问题内容: 为什么我们不能在非静态内部类中使用静态方法? 如果我将内部类设为静态,则可以工作。为什么? 问题答案: 因为内部类的实例与外部类的实例隐式关联,所以它本身不能定义任何静态方法。由于静态嵌套类无法直接引用其封闭类中定义的实例变量或方法,因此只能通过对象引用使用它们,因此在静态嵌套类中声明静态方法是安全的。

  • 我有点困惑。请看看下面的代码。 我确信调用此序列是可能的。 虽然我仍然有一个小小的困惑,但我们可以很容易地看到也调用方法,这是一个静态方法。方法 是调用非同步静态方法的静态同步方法。当 thread-2 获得类级锁时,为什么从 Thread-1 调用 没有被阻止? 我只是在逻辑上感到困惑,如果一个线程获得类级锁定,则该类其他非同步静态方法保持打开状态,以便从其他方法(实例方法)调用。为什么?

  • 问题内容: 我知道不可能在一个类中重写一个方法。但是,有没有办法使用非静态方法作为静态方法呢?例如,我有一个加数字的方法。我希望此方法在没有对象的情况下有用。是否可以在不创建其他方法的情况下执行类似的操作? 编辑:我的意思是,如果我将一个方法设为静态,我将需要它接受参数,如果我创建了一个已经设置了变量的对象,那么再次对具有相同参数的对象调用函数将非常不舒服。 我知道代码不正确,但是我想展示自己想做

  • PowerMockito.when(ConnectionFactory.getConn(“ABC”).getCurrentStatus()).thenReturn(ConnectionStatus.Connected); 对于上面的陈述,我得到了一个NPE。 我在junit测试类的开头已经有@PrepareForTest({fxallConnectionFactory.class,Connecti