繁体   English   中英

为什么编译器允许在静态上下文中创建非静态类的对象?

[英]Why the compiler allow to create an object of non static class in static context?

按照定义,非静态事物不能在静态上下文中进行访问,那么它如何允许为静态main方法创建非静态类的对象。

class Test
{
    static void Main()
    {
        Base x = new Derived();
        x.Foo();
    }
}

这不是正确的定义。 这是MSDN的一个:

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

请注意,您不是在访问的nont static成员Test ,而且您使用的是一些外部变量。

将以下内容与您的代码段进行比较:

class Test
{
    int t = 1;

    static void Main()
    {
        Base x = new Derived();
        x.Foo(t); // not allowed!
    }
}

您了解完全错误。 静态方法无法访问实例成员,因为它们没有this引用。 那不是禁止,仅仅是缺少参考的事实。 如果您提供引用(例如,通过方法参数,在内部创建实例等),则没有什么可以阻止您进行调用。

换一种说法,

class Test
{
  int testField = 23;

  static int GetTestField()
  {
    // Doesn't compile - static methods don't have a 
    // `this` reference to get the instance member
    return testField;
  }
}

无法编译。 然而,

public class Test
{
  public int testField = 23;
}

public static class Tester
{
  public static int GetTestField(Test test)
  {
    return test.testField;
  }
}

可以正常工作,因为您通过了要尝试访问的Test实例。

禁止在静态方法中使用实例成员(这将使它们几乎变得无用!)。 您只是没有this参考,就这么简单。

并不是真正的Stack Overflow,但是可以:

要了解这一点,了解幕后情况将很有帮助。 基本上,CPU实际上并没有诸如“类”或“方法”之类的概念。 这就是为什么高级编程语言发明了调用约定的原因-调用方法和传递参数的常用方法。 因此,例如,如果您想调用Print(23); ,它可能会执行以下操作:

push 23
call Print

然后, Print方法将知道其参数存储在堆栈的顶部,并且可以使用当前的堆栈指针来检索它。

当类出现时,他们带来了一个称为封装的概念。 基本上,该类实际上具有自己的内存存储和自己的方法。 当您要访问该数据(或功能)时,必须通过类实例进行操作。 在较低级别上,通常通过将引用作为方法的第一个参数传递给对象来处理。 因此,调用test.GetTestField(23, 21) (其中test是对Test类实例的引用)将执行以下操作:

push test
push 23
push 21
call Test.GetTestField

(这都是伪代码,编译器处理的实际方式在调用约定和语言之间有所不同;例如,参数通常以相反的顺序发送)

这样, GetTestField方法就可以访问Test类的实例(它可能需要也可以不需要它的功能)。 因此,当该方法获得一个实例字段的值,例如,它可以得到this.testField (在C#和大多数其他语言中, this可以中省略,并暗示-每次你访问一个实例字段/方法/等。一个类里面,它增加了this被窝里)。

静态方法没有这么奢侈-这就是为什么它们首先存在的原因。 它们用于与定义它们的类相关的功能,但是它们不需要该类的实例成员(或以其他方式获取实例引用)。

示例-.NET框架中有一个int类(实际上,它是一个struct ,但现在让我们忽略它)。 该类具有几个静态方法和几个实例方法。 例如,实例方法ToString()接受int的值,并将其转换为字符串值-为此,它(显然)需要具有整数值。 另一方面,它具有一个静态方法Parse(string) ,该方法采用一个字符串参数和一个整数的字符串值,并将其转换为整数。 它创建一个新的整数,并将字符串值解析为整数值。 由于它创建了一个新的integer实例,因此实际上并没有使用this ,因此可以安全地声明为static 这样至少避免了实例方法的一些额外费用(在默认情况下为虚拟方法的语言中,实例费用甚至更高),至少要传递额外的参数,但更重要的是,它广播了该方法的意图-“我不读或修改此类的实例成员”。 如果您没有静态方法,并且想编写上述的Parse方法,则必须先创建一个int实例,然后在其上调用Parse

您可以使用静态方法使非静态 class对象成为对象,但无法访问class中的non-static成员。 假设您有一个Derived类的对象作为Test的成员,那么您将无法在静态方法main中访问该对象。

在Test类中定义一个非静态方法,然后尝试从Main方法调用它,您将再次遇到错误。

class Test
{
    Derived d = new Derived();
    static void Main()
    {
      // You can not access d here.
        // You can not access MyFun() here.
    }
    void MyFun()
    {
    }
} 

您需要记住,您不是直接访问Foo()而是借助实例变量x 。这总是可能的。

注意:您不能访问非静态成员,如下所示:

void Foo()
{

}
static void Main()
{
   Foo();//compile error
}

只要您对对象有有效的引用, 就可以绝对地在静态上下文中访问非静态成员。

在这方面,静态上下文与非静态上下文不同的唯一原因是,静态上下文没有this引用(在您访问非静态成员而未明确引用对象时隐含this引用)。 另一方面,非静态上下文始终具有隐式this引用。

问题不在于您不能在静态上下文中访问非静态字段/方法/ etc,而是在于没有实例就无法访问非静态字段/方法。

在您所提供的代码, 实例Base ,所以你可以访问它的方法,不管上下文。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM