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

时间:2014-02-19 10:24:05

标签: c#

根据定义 非静态事物不能在静态上下文中访问 那么它如何允许为静态main方法创建一个非静态类的对象。

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

6 个答案:

答案 0 :(得分:3)

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

  

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

请注意,您没有访问Test的非静态成员,也没有使用某些外部变量。

将不允许的内容与您的代码段进行比较:

class Test
{
    int t = 1;

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

答案 1 :(得分:2)

你完全错了。静态方法无法访问实例成员,因为它们没有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),它接受​​带有整数字符串值的字符串参数,并将其转换为整数。它创建一个新的整数,并将字符串值解析为整数值。由于它创建了一个新的整数实例,因此它实际上并不使用this,因此可以安全地声明为static。这避免了实例方法的一些额外成本(在默认情况下方法是虚拟的语言中更是如此),至少传递额外参数,但更重要的是,它广播了方法的意图 - “我不读或修改此类的实例成员“。如果您没有静态方法,并且想要编写上面提到的Parse方法,则必须首先创建int的实例并在其上调用Parse。< / p>

答案 2 :(得分:1)

您可以在静态方法中创建非静态 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()
    {
    }
} 

答案 3 :(得分:1)

你需要记住,你没有直接访问Foo(),而是在实例变量x的帮助下。这总是可能的。

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

void Foo()
{

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

答案 4 :(得分:1)

只要你有一个对象的有效引用,你绝对可以访问静态上下文中的非静态成员。

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

答案 5 :(得分:1)

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

在您提供的代码中, Base的一个实例,因此可以访问它的方法,无论上下文如何。