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

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

6.08.2008 11:04:33
Пожалуйста, оставьте их открытыми для будущих улучшений.
Mark Biek 23.09.2008 17:49:16
Я думаю, что вопрос заключается в том, что C # нуждается в другом ключевом слове, именно для такой ситуации. Вам нужен метод, возвращаемое значение которого зависит только от типа, для которого он вызывается. Вы не можете назвать это «статическим», если указанный тип неизвестен. Но как только тип станет известным, он станет статичным. «Неразрешенная статика» - это идея - она ​​еще не статична, но как только мы узнаем тип приема, он будет. Это очень хорошая концепция, поэтому программисты постоянно просят об этом. Но это не совсем соответствовало тому, как дизайнеры думали о языке.
William Jockusch 20.03.2015 14:00:39
@WilliamJockusch, что означает получение типа? Если я вызываю BaseClass.StaticMethod (), то BaseClass - единственный тип, который он может использовать для принятия решения. Но на этом уровне он абстрактный, поэтому метод не может быть решен. Если вместо этого вы правильно вызываете DerivedClass.StaticMethod, то базовый класс не имеет значения.
Martin Capodici 9.02.2016 03:51:48
В базовом классе метод не разрешен, и вы не можете его использовать. Вам нужен либо производный тип, либо объект (который, в свою очередь, будет иметь производный тип). Вы должны иметь возможность вызывать baseClassObject.Method () или DerivedClass.Method (). Вы не можете вызвать BaseClass.Method (), потому что это не дает вам тип.
William Jockusch 9.02.2016 05:56:39
peterh - Reinstate Monica 10.01.2018 11:12:52
7 ОТВЕТОВ
РЕШЕНИЕ

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

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

155
15.04.2019 09:19:49
В сочетании с тем, как в C # выполняется перегрузка операторов, это, к сожалению, исключает возможность требовать от подклассов обеспечения реализации для данной перегрузки операторов.
Chris Moschini 20.03.2012 08:41:42
Я не нахожу этот ответ ужасно полезным, так как определение Test()в, Aа не абстрактное и потенциально определенное в B. \
user610650 31.05.2012 15:48:45
Параметры универсального типа эффективно ведут себя как непостоянные переменные типа, и в этом контексте могут быть полезны виртуальные статические методы. Например, если у кого-то был Carтип с CreateFromDescriptionметодом виртуальной статической фабрики, то код, который принимал Carобобщенный тип -ограниченный, Tмог бы вызывать T.CreateFromDescriptionавтомобиль типа T. Такая конструкция вполне могла бы поддерживаться в CLR, если бы каждый тип, который определил такой метод, содержал статический одноэлементный экземпляр универсального вложенного класса, который содержал виртуальные «статические» методы.
supercat 10.06.2013 22:27:10

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

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

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

44
6.08.2008 11:21:11
Учитывая ваш сценарий, я бы сказал, Base.GetNumber () вернет 5; Child1.GetNumber () возвращает 1; Child2.GetNumber () возвращает 2; Можете ли вы доказать, что я неправ, чтобы помочь мне понять ваши аргументы? Спасибо
Luis Filipe 2.10.2008 16:30:47
Лицо, которое вы считаете Base.GetNumber () возвращает 5, означает, что вы уже понимаете, что происходит. Возвращая базовое значение, наследование не происходит.
David Wengier 4.10.2008 22:54:10
Почему в мире Base.GetNumber () возвращает что-то еще, кроме 5? Это метод в базовом классе - там только один вариант.
Artem Russakovskii 17.06.2009 07:33:14
@ArtemRussakovskii: Предположим, кто-то имел int DoSomething<T>() where T:Base {return T.GetNumber();}. Казалось бы, полезно было DoSomething<Base>()бы вернуть пять, а DoSomething<Child2>()вернуло бы два. Такая способность была бы полезна не только для примеров игрушек, но и для чего-то подобного class Car {public static virtual Car Build(PurchaseOrder PO);}, где каждый класс, производный от него, Carдолжен был бы определить метод, который мог бы создать экземпляр с учетом заказа на покупку.
supercat 16.06.2013 20:23:33
Существует точно такая же «проблема» с нестатическим наследованием.
Ark-kun 8.01.2014 09:18:42

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

8
6.08.2008 11:24:41
C # статически типизирован; как я понимаю, вызовы полиморфных методов также связаны во время компиляции, то есть CLR не остается для определения, какой метод вызывать во время выполнения.
Adam Tolley 13.05.2011 21:23:49
Так как именно вы думаете, полиморфизм работает на CLR? Ваше объяснение только что исключило отправку виртуального метода.
Rytmis 14.05.2011 15:38:27
Это не такой полезный комментарий, как мог бы быть. Я пригласил (с «как я понимаю») полезный дискурс, думаю, что, возможно, вы могли бы предоставить немного больше контента - видя, как люди приходят сюда в поисках ответов, а не оскорблений. Хотя, кажется, я могу быть виноват в том же самом - я действительно имел в виду вышеупомянутый комментарий как вопрос: не оценивает ли C # эти вещи во время компиляции?
Adam Tolley 18.05.2011 18:17:02
Извинения, я не имел в виду оскорбление (хотя я признаю, что отвечал немного быстро ;-). Суть моего вопроса была в том, есть ли у вас эти классы: class Base {public virtual void Method (); } class Derived: Base {public override void Method (); } и напишите так: Base instance = new Derived (); instance.Method (); Информация о типе времени компиляции на сайте вызова состоит в том, что у нас есть экземпляр Base, когда фактическим экземпляром является Derived. Таким образом, компилятор не может определить точный метод для вызова. Вместо этого он генерирует инструкцию IL "callvirt", которая сообщает среде выполнения о необходимости отправки
Rytmis 19.05.2011 13:40:00
Спасибо человек, это информативно! Похоже, я достаточно долго откладывал погружение в IL, пожелай мне удачи.
Adam Tolley 1.06.2011 15:44:25

Другой респондент (Макдауэлл) сказал, что полиморфизм работает только для экземпляров объекта. Это должно быть квалифицировано; Есть языки, которые рассматривают классы как экземпляры типа «Класс» или «Метакласс». Эти языки поддерживают полиморфизм как для экземпляров, так и для классов (статических) методов.

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

17
9.08.2008 07:01:57

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

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

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

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

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

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

Надеюсь, пример был ясен.

5
18.08.2008 19:01:59

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

0
26.04.2011 01:07:05
-1 виртуальным методам не нужен экземпляр, кроме как по замыслу. И вы на самом деле не решаете вопрос, а лишь отклоняете его.
FallenAvatar 9.11.2013 22:29:34

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

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"?
8
8.01.2011 22:23:12
Нет, есть только один экземпляр массива 'leg'. Вывод является недетерминированным, так как вы не знаете, в каком порядке будут вызываться статические конструкторы (на самом деле нет никакой гарантии, что статический конструктор базового класса будет вызван вообще). «Потребность» - это довольно абсолютный термин, где «желание», вероятно, является более точным.
Sam 18.02.2010 06:16:16
legsдолжно быть статическим абстрактным свойством.
Little Endian 5.08.2016 20:31:42