【问题标题】:Why is object reference is always required in Main function to call a method of child class?为什么Main函数中总是需要对象引用才能调用子类的方法?
【发布时间】:2018-03-25 15:11:08
【问题描述】:

我想知道为什么Main方法中总是需要对象引用来调用子类的方法。

但是我们可以通过其他方法中的方法名直接调用Main 方法:

using System; 

class MainProgram : Parent_Class 
{
    static void Main(string[] args)
    {
        Project1_Child p = new Project1_Child(); 

        p.PI_add();   //->  Object reference is required     
    }


    public void non_Main_method() 
    {
        PI_add();     //-> No Object reference is required  

    }
}

public class Parent_Class
{


    public void PI_add()
    {

        Console.WriteLine("-------------");

    }

}

这是为什么?

【问题讨论】:

  • 那是因为 Main 是静态的。阅读静态方法,你会发现

标签: c# inheritance


【解决方案1】:

正如其他答案所指出的,您需要了解静态方法和实例方法之间的区别。这个答案的重点是让您走上正确术语的道路,并考虑对实例方法的调用。你说:

我想知道为什么在 Main 方法中总是需要一个对象引用来调用子类的方法。

让我们分解一下。

  • 调用是一种成员访问。也就是说,当您拨打电话时,您正在访问某个类型的成员。在这种情况下,您正在访问的成员是方法
  • 每个成员访问都有一个接收者。接收者是其成员被访问的事物。
  • 旁白:术语“receiver”可能看起来很混乱,为什么是“receiver”?当您访问某个类型的成员时,“收到”了什么?这个行话源于面向对象编程是关于向对象发送消息然后得到答案的想法。当您致电x = foo.Bar(123) 时,您正在向foo“发送消息”,告知您希望foo 执行什么计算,然后foo 将结果发回给您以输入x
  • 不要将接收者视为对象引用。 “对象”和“引用”在 C# 中都有非常具体的含义。接收者是一个表达式,类型检查器必须验证它是否具有某些属性。在某些情况下,接收者必须是一个有效的对象引用,但不是全部,正如我们将看到的。
  • 有些成员是“静态”的,有些是“实例”。不同之处在于:静态成员要求接收者为类型本身命名。实例成员要求接收者是该类型的实例。
  • 再次,我们遇到了一个行话问题。 “实例”是显而易见的,但“静态”不是。 “静态”真的没有充分的理由了。从历史上看,对静态成员的调用可以通过静态(即编译时)分析来确定。一些实例成员调用由运行时动态分派。 VB 将静态方法称为“共享”,这更有意义;它们在一个类型的实例中“共享”。
  • 这里有一个微妙之处,实例方法的接收者必须是该类型的有效实例。如果接收器是引用类型,那么它必须在运行时评估为非空引用。编译器只检查它的类型是否为编译器所知,而不是非空。这可能会在 C# 8 中改变!
  • 这里还有一个微妙之处,即值类型要求接收者不仅仅是一个,而是一个适当类型的变量。在某些情况下,C# 编译器会在必要时为您创建一个变量,但同样,这是一个您可以稍后了解的微妙之处。
  • “省略”是语言研究中的概念,我们可以从句子中“删除”不必要的单词并仍然理解它们。如果我说“你有需要洗的盘子吗?”即使在语法上它应该是“需要清洗的”,您也会理解这句话。
  • 现在我们来到了误解的症结所在。 C# 允许您在几种特定情况下省略接收器
  • 如果您正在访问的成员是实例成员,并且您正在从实例成员访问它,那么您可以省略接收者,C# 将假定您的意思是this.
  • 如果您正在访问的成员是静态成员,并且您正在从其类的任何成员访问它,那么您可以省略接收者,C# 将假定您的意思是,在这种情况下,MainProgram.

这就解释了你的观察结果。

  • Main 中,您调用PI_add。那是一个实例成员,所以它需要一个作为实例的接收器。如果您从实例成员调用 ,那么 C# 会假定您省略了 this.,但您是从静态成员调用的。所以需要receiver,省略是错误的。

  • 在实例方法non_main_method 中,您调用PI_add。它是一个实例方法,所以省略的接收者假定为this.

这些信息足以让您走上更有成效的道路。当您了解委托和扩展方法时,您会发现这里还有一些关于接收者到底是什么以及编译器如何处理它的细节。

【讨论】:

  • “这些信息足以让你走上一条更有成效的道路”。哈。这对于一篇博文来说已经足够了!再一次,你让我别无选择,只能投票赞成你的答案。
【解决方案2】:

因为main方法是静态方法,而non_Main_method是实例方法。

静态方法属于类型,不属于特定实例,所以要在其中执行实例方法,必须先创建该类型的实例。

来自static (C# Reference)

使用 static 修饰符声明一个静态成员,该成员属于类型本身而不是特定对象。

来自Static Classes and Static Class Members (C# Programming Guide)

静态方法和属性不能访问其包含类型中的非静态字段和事件,并且它们不能访问任何对象的实例变量,除非它在方法参数中显式传递。

尽管最后一部分不准确 - You are allowed to create an instance of the class inside a static method - 它不必作为参数传递。

【讨论】:

    【解决方案3】:

    调用类的非静态方法(如您的示例中的 PI_add())实际上只是

    的简写符号
    this.PI_add();
    

    而这又(基本上)只是调用(几乎)等效的静态方法(实际上并不存在于您的命名空间中)的简写符号

    PI_add_static(this);
    

    但是,如果您尝试从像 Main 这样的静态方法调用 PI_add(),则静态函数中永远不会有 this 对象。这是因为静态方法 - 根据定义 - 不属于任何对象。因此,您尝试做的事情相当于打电话

    PI_add_static(); // argument missing
    

    你错过了传递参数的地方。现在您可能很清楚,这是行不通的。

    顺便说一句:请注意,在将 PI_add_static(this) 与 this.PI_add() 进行比较时,我说的是“基本上”。事实上,使它们在现实中有所不同的是所有面向对象的核心:非静态方法能够在几个派生对象类型中选择(我们说分派)正确的实现。

    【讨论】:

      【解决方案4】:

      这与继承无关,简化问题并将方法PI_add移动到MainProgramSimply

      non_Main_method 是实例(非静态)方法,这意味着它共享当前对象的状态,以及它的所有实例成员,如PI_add 方法

      public void non_Main_method() 
          {
              PI_add();     //-> No Object reference is required  
          }
      

      是的,

      不需要对象引用

      因为调用实例成员不需要在同一类的另一个实例成员内的对象引用上调用,因为您将通过实例调用 non_Main_method(对吗?),但是,您可以使用 @ 987654327@ 关键字,表示:调用当前类实例的this成员:this.PI_add(); 但你可以省略它,因为你总是为当前实例编写。

      但另一方面,静态方法更可能是可在类级别而不是实例级别访问的全局成员,因此当您想要调用实例成员时,您需要指定您想要调用的具体实例.

      【讨论】:

        猜你喜欢
        • 2013-01-04
        • 2012-06-02
        • 1970-01-01
        • 2023-03-27
        • 1970-01-01
        • 2019-03-11
        • 2014-01-04
        • 2021-07-18
        • 1970-01-01
        相关资源
        最近更新 更多