[英]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.