Почему я не могу использовать абстрактные статические методы в С#?

В последнее время я работал с поставщиками, и я столкнулся с интересной ситуацией, когда я хотел иметь абстрактный класс, который имел абстрактный статический метод. Я прочитал несколько сообщений по этой теме, и это вроде как имело смысл, но есть ли четкое объяснение?

Ответ 1

Статические методы не создаются как таковые, они просто доступны без ссылки на объект.

Вызов статического метода выполняется через имя класса, а не через ссылку на объект, а код Intermediate Language (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.

Ответ 2

Статические методы не могут быть унаследованы или переопределены, и поэтому они не могут быть абстрактными. Поскольку статические методы определены в типе, а не в экземпляре класса, они должны быть явно указаны в этом типе. Поэтому, когда вы хотите вызвать метод в дочернем классе, вам нужно использовать его имя для его вызова. Это делает наследование неуместным.

Предположим, вы могли на мгновение наследовать статические методы. Представьте себе этот сценарий:

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(), какой метод будет вызываться? Какое значение возвращено? Его довольно легко увидеть, что без создания экземпляров объектов наследование довольно сложно. Абстрактные методы без наследования - это просто методы, которые не имеют тела, поэтому не могут быть вызваны.

Ответ 3

Другой респондент (McDowell) сказал, что полиморфизм работает только для экземпляров объектов. Это должно быть квалифицировано; существуют языки, которые рассматривают классы как экземпляры типа "Class" или "Metaclass". Эти языки поддерживают полиморфизм как для методов экземпляра, так и для класса (статического).

С#, как и Java и С++ перед ним, не является таким языком; ключевое слово static используется явно, чтобы обозначить, что метод статически привязан, а не динамический/виртуальный.

Ответ 4

Чтобы добавить к предыдущим объяснениям, вызовы статических методов привязаны к определенному методу во время компиляции, что скорее исключает полиморфное поведение.

Ответ 5

Вот ситуация, когда определенная потребность в наследовании для статических полей и методов:

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

Ответ 6

Мы фактически переопределяем статические методы (в delphi), это немного уродливо, но оно отлично подходит для наших нужд.

Мы используем его, чтобы классы могли иметь список своих доступных объектов без экземпляра класса, например, у нас есть метод, который выглядит так:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

Это уродливое, но необходимо, таким образом мы можем создать экземпляр именно того, что необходимо, вместо того, чтобы все классы, которые были созданы, просто искали доступные объекты.

Это был простой пример, но само приложение является клиент-серверным приложением, которое имеет все классы, доступные только на одном сервере, и несколько разных клиентов, которым может не понадобиться все, что сервер имеет и не будет нуждаться в экземпляре объекта.

Таким образом, это намного проще в обслуживании, чем одно приложение сервера для каждого клиента.

Надеюсь, что пример ясен.

Ответ 7

Абстрактные методы неявно виртуальны. Абстрактные методы требуют экземпляра, но статические методы не имеют экземпляра. Таким образом, вы можете иметь статический метод в абстрактном классе, он просто не может быть статическим абстрактным (или абстрактным статическим).

Ответ 8

Потому что дизайн С# был скопирован с Java. И Java не разрешает абстрактные статические методы.