Может ли статический метод быть виртуальным в C# и какие ограничения существуют
Что проверяют
Знаете ли вы, как устроена диспетчеризация вызовов в .NET, и следите ли за развитием языка. Классический ответ «нельзя» за последние годы стал неполным, и интервьюер ждёт, упомянете ли вы статические абстрактные члены интерфейсов. Следом обычно спрашивают про generic math.
Как отвечать
В классе нельзя. Модификаторы virtual, abstract и override к статическому методу класса неприменимы: виртуальный вызов выбирает реализацию по типу объекта во время выполнения через таблицу методов, а у статического вызова объекта нет, тип известен компилятору. В наследнике можно объявить статический метод с тем же именем через new, но это сокрытие, а не переопределение: какой метод вызовется, решает тип, указанный в коде.
В интерфейсе можно, начиная с C# 11 и .NET 7:
static abstract: интерфейс требует от реализующего типа статический член, напримерstatic abstract T Parse(string s);;static virtual: то же самое, но с реализацией по умолчанию в интерфейсе;- операторы: можно требовать
+,==и другие, на этом построен generic math:INumber<T>,IAdditionOperators<TSelf, TOther, TResult>.
Вызвать такой член можно только через параметр типа с ограничением: в static T Sum<T>(T[] xs) where T : INumber<T> внутри доступны T.Zero и оператор +. Реализация выбирается по конкретному T, для значимых типов JIT генерирует специализированный код без боксинга.
Ограничения: обратиться к IParsable<T>.Parse через имя интерфейса нельзя, нужен параметр типа. Интерфейс со статическими абстрактными членами нельзя подставить как аргумент типа сам по себе. А в классах статические методы по-прежнему не переопределяются.
Где спотыкаются
Отвечают «статика не может быть виртуальной» и останавливаются. Для современного .NET это ответ наполовину: generic math входит в стандартную библиотеку с .NET 7.
Путают сокрытие через new с переопределением и ждут, что вызов через базовый тип попадёт в метод наследника.
Утверждают, что статический метод не может вызывать виртуальные. Может, если получит экземпляр параметром: у него нет только this, а виртуальный вызов на переданном объекте работает как обычно.