为什么在Main函数中总是需要对象引用来调用子类的方法?

时间:2018-03-25 15:11:09

标签: c# inheritance

我想知道为什么在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("-------------");

    }

}

为什么会这样?

4 个答案:

答案 0 :(得分:7)

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

  

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

让我们分解。

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

然后解释了你的观察结果。

  • Main中,您拨打PI_add。那是一个实例成员,所以它需要一个接收器作为实例。如果您从中调用实例成员,则C#会认为您已将this.省略,但您是从静态成员调用的。因此接收器是必需的,忽略接收器是错误的。

  • non_main_method实例方法中,您调用PI_add。它是一种实例方法,因此假设省略的接收者为this.

这足以让您走上更高效的道路。当您了解委托和扩展方法时,您会发现这里有更多的细微之处,关于接收器究竟是什么以及编译器如何处理它。

答案 1 :(得分:0)

因为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 - 但它不必作为参数传递。

答案 2 :(得分:0)

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

的简写符号
this.PI_add();

反过来(基本上)只是一个简写符号,用于调用(几乎)等效的静态方法(它实际上并不存在于你的命名空间中)

PI_add_static(this);

但是如果你试图从像Main这样的静态方法调用PI_add(),那么在静态函数中永远不会有这个对象。这是因为静态方法 - 按定义 - 不属于任何对象。因此,您要做的就是致电

PI_add_static(); // argument missing

你错过了传递参数的地方。现在可能很清楚,这不起作用。

顺便说一句:请注意,我说'#34;基本上"将PI_add_static(this)与this.PI_add()进行比较时。实际上,它们在现实中的不同之处在于所有面向对象的核心:非静态方法在几个派生对象类型中选择(我们称调度)正确实现的能力。

答案 3 :(得分:0)

这与继承无关,可以简化问题并将方法PI_add移至MainProgramSimply

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

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

是的,

  

无需对象引用

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

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