我最近一直在使用提供者,我遇到了一个有趣的情况,我想有一个抽象类,它有一个抽象静态方法。我读了一些关于这个话题的帖子,它有点说得通,但有一个清晰的解释吗?


当前回答

它目前作为预览特性在c# 10中可用。

其他回答

另一位受访者(McDowell)说多态性只适用于对象实例。应该是有资格的;有些语言确实将类视为“类”或“元类”类型的实例。这些语言确实支持实例和类(静态)方法的多态性。

c#,就像之前的Java和c++一样,不是这样一种语言;static关键字显式地用于表示该方法是静态绑定的,而不是动态/虚拟的。

这里有一种情况,肯定需要继承静态字段和方法:

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"?

在。net 6 / c# 10/next/preview中,你可以通过“接口中的静态抽象成员”来做到这一点。

(在编写代码时编译成功,但一些ide有突出显示代码的问题)

SharpLab演示

using System;

namespace StaticAbstractTesting
{
    public interface ISomeAbstractInterface
    {
        public abstract static string CallMe();
    }

    public class MyClassA : ISomeAbstractInterface
    {
        static string ISomeAbstractInterface.CallMe()
        {
            return "You called ClassA";
        }
    }

    public class MyClassB : ISomeAbstractInterface
    {
        static string ISomeAbstractInterface.CallMe()
        {
            return "You called ClassB";
        }
    }

    public class Program
    {

        public static void Main(string[] args)
        {
            UseStaticClassMethod<MyClassA>();
            UseStaticClassMethod<MyClassB>();
        }

        public static void UseStaticClassMethod<T>() where T : ISomeAbstractInterface
        {
            Console.WriteLine($"{typeof(T).Name}.CallMe() result: {T.CallMe()}");
        }
    }
}

由于这是运行时中的一个主要更改,因此产生的IL代码看起来也非常干净,这意味着这不仅仅是语法糖。

public static void UseStaticClassMethodSimple<T>() where T : ISomeAbstractInterface {
IL_0000: constrained. !!T
IL_0006: call string StaticAbstractTesting.ISomeAbstractInterface::CallMe()
IL_000b: call void [System.Console]System.Console::WriteLine(string)
IL_0010: ret
}

资源:

https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/static-abstract-interface-methods https://github.com/dotnet/csharplang/issues/4436

它目前作为预览特性在c# 10中可用。

静态方法不是这样实例化的,它们只是在没有对象引用的情况下可用。

对静态方法的调用是通过类名完成的,而不是通过对象引用,调用它的中间语言(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.t test,并在运行时解析调用,但它仍然不会是虚的,因为您仍然必须在那里编写某种类名。

虚方法和抽象方法只有在使用一个在运行时可以包含许多不同类型对象的变量时才有用,因此您希望为变量中的当前对象调用正确的方法。对于静态方法,无论如何都需要遍历类名,因此要调用的确切方法在编译时就知道了,因为它不能也不会改变。

因此,虚拟/抽象静态方法在. net中是不可用的。