在回答这个问题时,普遍的共识是静态方法不应被覆盖(因此C#中的静态函数不能是虚拟的或抽象的)。但是,不仅在C#中是这种情况。Java也禁止这样做,C ++也似乎不喜欢它。但是,我可以想到许多我想在子类中覆盖的静态函数示例(例如,工厂方法)。从理论上讲,有多种方法可以解决这些问题,但都不是干净或简单的方法。
为什么静态函数不应该被覆盖?
在回答这个问题时,普遍的共识是静态方法不应被覆盖(因此C#中的静态函数不能是虚拟的或抽象的)。但是,不仅在C#中是这种情况。Java也禁止这样做,C ++也似乎不喜欢它。但是,我可以想到许多我想在子类中覆盖的静态函数示例(例如,工厂方法)。从理论上讲,有多种方法可以解决这些问题,但都不是干净或简单的方法。
为什么静态函数不应该被覆盖?
Answers:
对于静态方法,没有对象提供对替代机制的适当控制。
普通的类/实例虚拟方法机制允许对覆盖进行微调控制,如下所示:每个实际对象都是一个类的实例。该类确定替代的行为。在虚拟方法中,它始终是第一个漏洞。然后,它可以选择在正确的时间调用父方法进行实现。然后,每个父方法都可以轮流调用其父方法。这导致父级调用很好地级联,从而实现了面向对象的代码重用概念之一。(在这里,基类/超级类的代码以相对复杂的方式被重用; OOP中代码重用的另一个正交概念是简单地具有相同类的多个对象。)
基类可以被各种子类重用,并且每个子类都可以舒适地共存。用于实例化对象的每个类指示自己的行为,并与其他对象和平并存。客户端可以通过选择要用于实例化对象的类并根据需要传递给其他对象来控制其所需的行为以及何时进行控制。
(这不是一种完美的机制,因为人们总是可以识别出不支持的功能,这就是为什么工厂方法和依赖项注入之类的模式位于最顶层的原因。)
因此,如果我们要在不更改其他任何特征的情况下为静态变量提供覆盖功能,则将难以订购覆盖。很难为覆盖的适用性定义一个有限的上下文,因此您将全局获取覆盖,而不是像对象那样在本地获取覆盖。没有实例对象可以切换行为。因此,如果有人调用了恰好被另一个类重写的静态方法,则该重写是否应获得控制?如果存在多个此类替代,谁先控制了谁?第二?使用实例对象覆盖,这些问题都有有意义且合理的答案,而使用静态对象则没有。
覆盖静态变量会非常混乱,而且以前已经做过类似的事情。
例如,Mac OS System 7及更高版本使用陷阱修补程序机制,通过在操作系统之前控制对应用程序进行的系统调用来扩展系统。您可以将系统调用补丁程序表视为函数指针数组,非常类似于实例对象的vtable,只是它是单个全局表。
由于陷阱修补程序的无序性质,这给程序员带来了不尽的悲伤。最后一个打补丁的人基本上会赢,即使他们不想打。陷阱的每个修补程序都将捕获先前的陷阱值,以用于某种父调用功能,这是非常脆弱的。删除陷阱补丁程序,比如说当您不再需要了解系统调用时,就被认为是不好的形式,因为您实际上没有删除补丁程序所需的信息(如果这样做了,您还将取消对随后的其他任何补丁程序的补丁程序您)。
这并不是说不可能创建覆盖静态的机制,而是我可能更愿意做的是将静态字段和静态方法转换为元类的实例字段和实例方法,以便普通对象然后将应用定向技术。请注意,有些系统也可以这样做:CSE 341:Smalltalk类和元类;另请参阅:与Java静态等效的Smalltalk是什么?
我想说的是,您必须进行一些认真的语言功能设计才能使其在合理范围内正常工作。举一个例子,一个幼稚的方法确实做到了,并一无所获,但是却是一个很大的问题,而且可以说(即我认为)通过提供不完整且难以使用的抽象而在架构上存在缺陷。
当您完成静态替代功能的设计以使其正常工作时,您可能刚刚发明了某种形式的元类,这是OOP对基于类的方法的自然扩展。因此,没有理由不这样做-有些语言实际上是这样做的。许多语言选择不这样做可能只是附带要求。
this.instanceMethod()
具有动态分辨率,因此,如果self.staticMethod()
具有相同的分辨率(即动态),那么它将不再是静态方法。
覆盖取决于虚拟调度:您可以使用this
参数的运行时类型来决定要调用的方法。静态方法没有this
参数,因此没有派生对象。
某些语言(尤其是Delphi和Python)具有“介于两者之间”的范围,可以实现以下目的:类方法。 类方法不是普通的实例方法,但也不是静态的。它接收一个self
参数(有趣的是,两种语言都调用this
parameter self
),该参数是对对象类型本身的引用,而不是对该类型实例的引用。使用该值,现在您可以使用类型进行虚拟调度。
不幸的是,JVM和CLR都没有可比的东西。
self
具有可重写方法的类作为实际实例化对象的类的语言-当然是Smalltalk,Ruby,Dart,Objective C,Self,Python?与C ++之后的语言相比,即使有一些反射访问,类也不是一流的对象?
virtual
像实例方法一样,显式声明一个类方法,然后在后代类中对其进行重写。
你问
为什么静态函数不应该被覆盖?
我问
为什么要覆盖的函数应该是静态的?
某些语言会强制您以静态方法开始播放。但是之后,您真的可以解决很多问题,而无需使用任何其他静态方法。
有些人喜欢在不依赖对象状态的任何时候使用静态方法。有些人喜欢使用静态方法来构造其他对象。有些人喜欢尽可能避免使用静态方法。
这些人没有错。
如果您需要使其可重写,则停止将其标记为静态。一切都不会中断,因为您有一个无状态的物体飞来飞去。
_start()
Linux上的符号)。在该示例中,可执行文件是对象(它具有自己的“调度表”和所有内容),入口点是动态调度的操作。因此,从外部查看时,程序的入口点本质上是虚拟的,从内部查看时,也很容易使它看起来像虚拟的。
为什么静态方法不能被重写?
这不是“应该”的问题。
“覆盖”是指“ 动态分配”。“静态方法”是指“静态分派”。如果某些内容是静态的,则不能覆盖它。如果可以覆盖某些内容,则它不是静态的。
您的问题有点像问:“为什么三轮车不能有四个轮子?” “三轮车” 的定义是具有三个轮子。如果是三轮车,则不能有四个轮子,如果是四个轮,则不能是三轮车。同样,“静态方法” 的定义是静态分配的。如果它是静态方法,则不能动态调度,如果可以动态调度,则不能是静态方法。
当然,完全有可能会覆盖类方法。或者,您可以使用Ruby之类的语言,其中类是与其他任何对象一样的对象,因此可以具有实例方法,从而完全消除了对类方法的需要。(Ruby只有一种方法:实例方法。它没有类方法,静态方法,构造函数,函数或过程。)
因此,C#中的静态函数不能是虚拟的或抽象的
在C#中,您始终使用类来调用静态成员,例如BaseClass.StaticMethod()
not baseObject.StaticMethod()
。因此,最终,如果您ChildClass
继承自BaseClass
和childObject
的实例ChildClass
,则将无法从调用静态方法childObject
。您将始终需要显式使用真实的类,因此static virtual
公正是没有道理的。
您可以做的是static
在子类中重新定义相同的方法,并使用new
关键字。
class BaseClass {
public static int StaticMethod() { return 1; }
}
class ChildClass {
public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}
int result;
var baseObj = new BaseClass();
var childObj = new ChildClass();
result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE
result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE
如果可以致电baseObject.StaticMethod()
,那么您的问题就很有意义了。
self
指向该类而不是该类实例的指针。