我最近一直在与提供程序一起工作,遇到了一个有趣的情况,我想拥有一个抽象类,该抽象类具有抽象的静态方法。我阅读了有关该主题的几篇文章,这很有道理,但是有一个很好的清晰解释吗?
我最近一直在与提供程序一起工作,遇到了一个有趣的情况,我想拥有一个抽象类,该抽象类具有抽象的静态方法。我阅读了有关该主题的几篇文章,这很有道理,但是有一个很好的清晰解释吗?
Answers:
静态方法不是这样实例化的,它们仅在没有对象引用的情况下可用。
静态方法的调用是通过类名完成的,而不是通过对象引用完成的,调用该方法的中间语言(IL)代码将通过定义该方法的类的名称(而不一定是其名称)来调用抽象方法。您使用的课程。
让我举一个例子。
使用以下代码:
public class A
{
public static void Test()
{
}
}
public class B : A
{
}
如果您致电B.Test,如下所示:
class Program
{
static void Main(string[] args)
{
B.Test();
}
}
那么Main方法中的实际代码如下:
.entrypoint
.maxstack 8
L0000: nop
L0001: call void ConsoleApplication1.A::Test()
L0006: nop
L0007: ret
如您所见,调用是对A.Test的,因为它是定义它的A类,而不是对B.Test的,即使您可以用这种方式编写代码。
如果您有类类型(例如在Delphi中),您可以在其中创建一个引用类型而不是对象的变量,那么您将更多地使用虚拟方法和抽象静态方法(以及构造方法),但它们将不可用,并且因此,静态调用在.NET中不是虚拟的。
我意识到IL设计器可以允许编译代码以调用B.Test,并在运行时解决该调用,但是它仍然不是虚拟的,因为您仍然必须在其中编写某种类名。
虚拟方法以及抽象方法仅在使用变量时才有用,该变量在运行时可以包含许多不同类型的对象,因此您要为变量中的当前对象调用正确的方法。对于静态方法,无论如何都需要使用一个类名,因此在编译时就知道要调用的确切方法,因为它不能并且不会改变。
因此,.NET中不提供虚拟/抽象静态方法。
Car
类型具有虚拟静态CreateFromDescription
工厂方法,则接受一个Car
受约束的通用类型的代码T
可以调用T.CreateFromDescription
以生成type的汽车T
。如果定义此方法的每个类型都持有嵌套类的静态单例实例,而该嵌套类泛型持有虚拟的“静态”方法,则可以在CLR中很好地支持这种构造。
静态方法不能被继承或覆盖,这就是为什么它们不能抽象的原因。由于静态方法是在类的类型而非实例上定义的,因此必须在该类型上显式调用它们。因此,当您想在子类上调用方法时,需要使用其名称来调用它。这使得继承无关紧要。
假设您可以暂时继承静态方法。想象一下这种情况:
public static class Base
{
public static virtual int GetNumber() { return 5; }
}
public static class Child1 : Base
{
public static override int GetNumber() { return 1; }
}
public static class Child2 : Base
{
public static override int GetNumber() { return 2; }
}
如果调用Base.GetNumber(),将调用哪个方法?返回哪个值?很容易看出,不创建对象实例,继承就很难。没有继承的抽象方法就是没有主体的方法,因此无法调用。
int DoSomething<T>() where T:Base {return T.GetNumber();}
。如果DoSomething<Base>()
可以返回五个,而DoSomething<Child2>()
返回两个,则似乎很有用。这种能力不仅对玩具示例有用,而且对类似的东西也有用,在这种情况下class Car {public static virtual Car Build(PurchaseOrder PO);}
,每个派生的类Car
都必须定义一个方法,该方法可以在给定购买订单的情况下构建实例。
在这种情况下,绝对需要静态字段和方法的继承:
abstract class Animal
{
protected static string[] legs;
static Animal() {
legs=new string[0];
}
public static void printLegs()
{
foreach (string leg in legs) {
print(leg);
}
}
}
class Human: Animal
{
static Human() {
legs=new string[] {"left leg", "right leg"};
}
}
class Dog: Animal
{
static Dog() {
legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
}
}
public static void main() {
Dog.printLegs();
Human.printLegs();
}
//what is the output?
//does each subclass get its own copy of the array "legs"?
legs
应该是静态的抽象属性。
为了补充前面的解释,静态方法调用在编译时绑定到特定的方法,而是排除了多态行为。
我们实际上重写了静态方法(在delphi中),这有点丑陋,但它可以很好地满足我们的需求。
我们使用它,以便类可以在没有类实例的情况下获得其可用对象的列表,例如,我们有一个如下所示的方法:
class function AvailableObjects: string; override;
begin
Result := 'Object1, Object2';
end;
这很丑陋,但是很必要,这样我们可以实例化所需的东西,而不是实例化所有类以仅搜索可用的对象。
这是一个简单的示例,但是应用程序本身是一个客户端-服务器应用程序,仅在一台服务器上具有所有可用的类,并且多个不同的客户端可能不需要服务器拥有的所有内容,也永远不需要对象实例。
因此,与为每个客户端使用一个不同的服务器应用程序相比,维护起来容易得多。
希望这个例子很清楚。
抽象方法是隐式虚拟的。抽象方法需要一个实例,但是静态方法没有实例。因此,您可以在抽象类中有一个静态方法,但不能只是静态抽象(或抽象静态)。