У меня есть вспомогательный класс, который представляет собой просто набор статических методов, и я хотел бы создать подкласс вспомогательного класса. Некоторое поведение уникально в зависимости от подкласса, поэтому я хотел бы вызвать виртуальный метод из базового класса, но поскольку все методы статичны, я не могу создать простой виртуальный метод (нужна ссылка на объект для доступа к виртуальному методу).
Есть ли способ обойти это? Полагаю, я мог бы использовать синглтон .. HelperClass.Instance.HelperMethod () не намного хуже, чем HelperClass.HelperMethod (). Брауни указывает на любого, кто может указать на некоторые языки, поддерживающие виртуальные статические методы.
Редактировать: Ладно, я сумасшедший. Результаты поиска Google заставили меня подумать, что меня там немного не было.
Я не хочу создавать экземпляр объекта везде, где используется этот вспомогательный метод.
Затем используйте одноэлементную инъекцию или внедрение зависимостей, в зависимости от того, что имеет смысл.
Возможный дубликат Почему я не могу использовать абстрактные статические методы в C#?
Возможный дубликат Как реализовать виртуальные статические свойства?
В stackoverflow.com/a/66070907/6053778 я описываю шаблон, который, как мне кажется, может имитировать желаемое поведение.





Виртуальные статические методы не имеют смысла. Если я вызываю HelperClass.HelperMethod();, почему я должен ожидать вызова некоторого случайного метода подкласса? Решение действительно не работает, когда у вас есть 2 подкласса HelperClass - какой из них вы бы использовали?
Если вы хотите иметь переопределяемые методы статического типа, вам, вероятно, следует использовать:
Выберите то решение, которое больше подходит для вашей ситуации.
-1 Потому что вы забыли про дженерики. T.Add (слева, справа); так много смысла. Не так ли?
И как бы вы переопределили свою реализацию в подклассе?
Он может быть реализован как член виртуального экземпляра типа класса. Типы подклассов будут наследовать от типа класса, что позволит им переопределить.
Да, но реализация подкласса не будет доступна из базового типа в статическом контексте, что и было задано в исходном вопросе.
Да, они имеют смысл, это просто вопрос семантики. Я бы сказал, что если есть смысл объявлять статический метод внутри класса, имеет смысл разрешить переопределение его для каждого типа. См. Примеры Delphi в этой теме. Вы можете возразить, что «статика» имеет определенное значение; ну, в C он имеет совсем другое значение, чем, например, в C# и Java.
@etrusco прав. В других языках, например в Python, есть виртуальные статические методы. На этих языках объект доступен для динамической отправки, но не передается методу (поскольку метод является статическим).
Да, это имеет смысл. Это не следует отмечать как ответ.
статический метод существует вне экземпляра класса. Он не может использовать нестатические данные.
виртуальный метод будет «перезаписан» перегруженной функцией в зависимости от тип экземпляра.
Итак, у вас есть явное противоречие между статикой и виртуальностью.
Это не проблема поддержки, это концепция.
Обновлять: Здесь я ошибся (см. Комментарии):
So I doubt you will find any OOP-Language which will support virtual static methods.
Виртуальный метод может быть перезаписан по типу экземпляра. Все подклассы могут использовать одну и ту же реализацию перегруженной функции. Вот почему вы хотите сделать их ОБА статичными и виртуальными. Это позднее статическое связывание, доступное в PHP5.
Нет, здесь нет противоречия, это полностью вопрос поддержки компилятора, и многие языки, такие как Python, делают это. Они делают это, посещая первоклассные классы. То есть классы, которые сами по себе являются объектами, которые могут быть присвоены переменным и вызывать их методы. (Чтобы узнать больше, введите запрос "classmethod".)
Object Pascal (Delphi и эквивалент Lazarus с открытым исходным кодом) всегда их поддерживал.
Я слышал, что Delphi поддерживает нечто подобное. Кажется, он делает это, создавая экземпляры объектов классов метакласса.
Я не видел, чтобы это работало, поэтому я не уверен, что это работает, или какой в этом смысл.
P.S. Пожалуйста, поправьте меня, если я ошибаюсь, так как это не мой домен.
Действительно, это можно сделать в Delphi. Пример:
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
end;
TTestClass = class
public
class procedure TestMethod(); virtual;
end;
TTestDerivedClass = class(TTestClass)
public
class procedure TestMethod(); override;
end;
TTestMetaClass = class of TTestClass;
var
Form1: TForm1;
implementation
{$R *.dfm}
class procedure TTestClass.TestMethod();
begin
Application.MessageBox('base', 'Message');
end;
class procedure TTestDerivedClass.TestMethod();
begin
Application.MessageBox('descendant', 'Message');
end;
procedure TForm1.FormShow(Sender: TObject);
var
sample: TTestMetaClass;
begin
sample := TTestClass;
sample.TestMethod;
sample := TTestDerivedClass;
sample.TestMethod;
end;
Довольно интересно. Я больше не использую Delphi, но помню, как мог очень легко создавать различные типы элементов управления на настраиваемом холсте конструктора с помощью функции метакласса: класса управления, например. TButton, TTextBox и т. д. Были параметрами, и я мог вызвать соответствующий конструктор, используя фактический аргумент метакласса.
Вид заводской выкройки бедняги :)
Тот факт, что C# не поддерживает статические виртуальные методы, сводит меня с ума ... особенно учитывая, что C# был разработан тем же парнем, который проектировал delphi 10 лет назад (Андерс Хейлсберг)
Поскольку виртуальный метод использует определенный тип созданного объекта, чтобы определить, какую реализацию выполнить (в отличие от объявленного типа ссылочной переменной)
... и static, конечно, все о том, чтобы не заботиться о том, есть ли вообще экземпляр класса ...
Итак, они несовместимы.
Итог: если вы хотите изменить поведение в зависимости от того, к какому подклассу относится экземпляр, тогда методы должны быть виртуальными методами базового класса, а не статическими.
Но, поскольку у вас уже есть эти статические методы, и теперь вам нужно их переопределить, вы можете решить свою проблему следующим образом: Добавьте к базовому классу методы виртуального экземпляра, которые просто делегируют статические методы, а затем переопределяют эти методы оболочки виртуального экземпляра (не статические) в каждом производном подклассе, если это необходимо ...
Вы не сошли с ума. То, что вы имеете в виду, называется поздним статическим связыванием; он был недавно добавлен в PHP. Есть отличная ветка, которая описывает это - здесь: Когда вам нужно использовать позднюю статическую привязку?
Фактически возможно объединить виртуальное и статическое для метода или члена, используя ключевое слово new вместо virtual.
Вот пример:
class Car
{
public static int TyreCount = 4;
public virtual int GetTyreCount() { return TyreCount; }
}
class Tricar : Car
{
public static new int TyreCount = 3;
public override int GetTyreCount() { return TyreCount; }
}
...
Car[] cc = new Car[] { new Tricar(), new Car() };
int t0 = cc[0].GetTyreCount(); // t0 == 3
int t1 = cc[1].GetTyreCount(); // t1 == 4
Очевидно, что значение TyreCount могло быть установлено в замещенном методе GetTyreCount, но это позволяет избежать дублирования значения. Получить значение можно как из класса, так и из экземпляра класса.
Может ли кто-нибудь найти действительно разумное использование этой функции?
@Mart: Я чуть не проголосовал за вас, пока не понял, что у вас просто плохой пример. Вместо статических полей измените пример, чтобы использовать статические методы, где производный класс вызывает метод базового класса, а затем делает что-то еще.
Я не думаю, что ты сумасшедший. Вы просто хотите использовать то, что в настоящее время невозможно в .NET.
Ваш запрос на виртуальный статический метод будет иметь такой смысл, если мы говорим о дженериках.
Например, моя будущая просьба к дизайнерам CLR - разрешить мне писать такие интерфейсы:
public interface ISumable<T>
{
static T Add(T left, T right);
}
и используйте это так:
public T Aggregate<T>(T left, T right) where T : ISumable<T>
{
return T.Add(left, right);
}
Но сейчас это невозможно, поэтому делаю так:
public static class Static<T> where T : new()
{
public static T Value = new T();
}
public interface ISumable<T>
{
T Add(T left, T right);
}
public T Aggregate<T>(T left, T right) where T : ISumable<T>, new()
{
return Static<T>.Value.Add(left, right);
}
@SeeR: -1: Я исправлю это, если ошибаюсь, но это не решает исходную проблему.
@John Saunders: Ему нужны статические виртуальные методы, но это невозможно - он должен использовать для этого методы экземпляра. Он также не хочет создавать экземпляр этого класса каждый раз, когда он хочет использовать этот статический (теперь экземплярный) метод - вот почему я создал класс Static <T>. Теперь у него будет только один экземпляр своего класса для всего приложения. Думаю, это приемлемый налог за такую функциональность. Метод Aggregate <T> - это просто пример того, как он может его использовать. Итак, в итоге у нас есть статическая виртуальная замена в C# - разве не об этом запросе?
@SeeR: Ваш подход работает нормально, если рассматриваемый тип удовлетворяет ограничению new. Статические виртуальные методы позволили бы использовать такие конструкции безопасным для типов способом с типами, которые не удовлетворяли ограничению new. В качестве альтернативы можно было бы, чтобы класс Static не имел ограничения new, а скорее попытался бы использовать Reflection для создания объекта со специальным параметризованным конструктором. К сожалению, я не знаю способа сделать это типобезопасным.
Я пришел из Delphi, и это одна из многих функций, которых мне очень не хватает в C#. Delphi позволит вам создавать ссылки на типизированные типы, и вы можете передавать тип производного класса везде, где требуется тип родительского класса. Такое обращение с типами как с объектами имело огромную пользу. В частности, позволяет определять метаданные во время выполнения. Я ужасно смешиваю синтаксис здесь, но в C# это будет выглядеть примерно так:
class Root {
public static virtual string TestMethod() {return "Root"; }
}
TRootClass = class of TRoot; // Here is the typed type declaration
class Derived : Root {
public static overide string TestMethod(){ return "derived"; }
}
class Test {
public static string Run(){
TRootClass rc;
rc = Root;
Test(rc);
rc = Derived();
Test(rc);
}
public static Test(TRootClass AClass){
string str = AClass.TestMethod();
Console.WriteLine(str);
}
}
would produce: Root derived
Март понял это с помощью ключевого слова «новое». На самом деле я попал сюда, потому что мне нужна была такая функциональность, и решение Mart отлично работает. Фактически, я сделал это лучше и сделал свой метод базового класса абстрактным, чтобы заставить программиста предоставить это поле.
Мой сценарий был таким:
У меня базовый класс HouseDeed. У каждого типа дома, производного от HouseDeed, должна быть цена.
Вот частичный базовый класс HouseDeed:
public abstract class HouseDeed : Item
{
public static int m_price = 0;
public abstract int Price { get; }
/* more impl here */
}
Теперь давайте посмотрим на два производных типа домов:
public class FieldStoneHouseDeed : HouseDeed
{
public static new int m_price = 43800;
public override int Price { get { return m_price; } }
/* more impl here */
}
и...
public class SmallTowerDeed : HouseDeed
{
public static new int m_price = 88500;
public override int Price { get { return m_price; } }
/* more impl here */
}
Как видите, я могу получить доступ к цене дома через тип SmallTowerDeed.m_price и экземпляр new SmallTowerDeed (). Price И будучи абстрактным, этот механизм заставляет программиста указывать цену для каждого нового производного типа дома.
Кто-то указал на то, что «статическое виртуальное» и «виртуальное» концептуально противоречат друг другу. Я не согласен. В этом примере статические методы не нуждаются в доступе к данным экземпляра, поэтому выполняются требования, чтобы (1) цена была доступна только через TYPE и (2) была указана цена.
Этот ответ только кажется косвенно связанным с вопросом. К тому же это довольно плохой пример. m_price является деталью реализации и ничего не добавляет, будучи частью HouseDeed. Было бы гораздо лучше удалить его из HouseDeed и сделать m_price приватным в обоих подклассах. Тот же результат без злоупотребления ключевым словом «новое».
Вы можете использовать новое ключевое слово
namespace AspDotNetStorefront
{
// This Class is need to override StudioOnlineCommonHelper Methods in a branch
public class StudioOnlineCommonHelper : StudioOnlineCore.StudioOnlineCommonHelper
{
//
public static new void DoBusinessRulesChecks(Page page)
{
StudioOnlineCore.StudioOnlineCommonHelper.DoBusinessRulesChecks(page);
}
}
}
Вы можете добиться того же эффекта, просто используя обычный статический метод, а затем затеняя его с помощью ключевого слова new.
public class Base
{
//Other stuff
public static void DoSomething()
{
Console.WriteLine("Base");
}
}
public class SomeClass : Base
{
public new static void DoSomething()
{
Console.WriteLine("SomeClass");
}
}
public class SomeOtherClass : Base
{
}
Затем вы можете вызвать такие методы
Base.DoSomething(); //Base
SomeClass.DoSomething(); //SomeClass
SomeOtherClass.DoSomething(); //Base
Это может иметь непредвиденные последствия. Например, если в вашем базовом классе есть другой метод, вызывающий DoSomething, и вы вызываете SomeClass.ThatOtherMethod (), этот метод вызывает DoSomething базы, а не версию SomeClass. dotnetfiddle.net/scRWKD
бросить NotImplementedException в базовый класс, чтобы убедиться, что ничто не вызывает это
Метод переопределения предоставляет новую реализацию члена, унаследованного от базового класса. Метод, переопределенный объявлением переопределения, известен как переопределенный базовый метод. Переопределенный базовый метод должен иметь ту же сигнатуру, что и переопределенный метод. Вы не можете переопределить не виртуальный или статический метод. Переопределенный базовый метод должен быть виртуальным, абстрактным или переопределяемым.
Объявление переопределения не может изменить доступность виртуального метода. И метод переопределения, и виртуальный метод должны иметь один и тот же модификатор уровня доступа.
Вы не можете использовать модификаторы new, static или virtual для изменения метода переопределения.
Объявление переопределяющего свойства должно указывать точно такой же модификатор доступа, тип и имя, что и унаследованное свойство, а переопределяемое свойство должно быть виртуальным, абстрактным или переопределяемым.
Существует способ принудительного наследования «абстрактных статических» методов от абстрактного универсального класса. См. Следующее:
public abstract class Mother<T> where T : Mother<T>, new()
{
public abstract void DoSomething();
public static void Do()
{
(new T()).DoSomething();
}
}
public class ChildA : Mother<ChildA>
{
public override void DoSomething() { /* Your Code */ }
}
public class ChildB : Mother<ChildB>
{
public override void DoSomething() { /* Your Code */ }
}
Пример (с использованием предыдущей Матери):
public class ChildA : Mother<ChildA>
{
public override void DoSomething() { Console.WriteLine("42"); }
}
public class ChildB : Mother<ChildB>
{
public override void DoSomething() { Console.WriteLine("12"); }
}
public class Program
{
static void Main()
{
ChildA.Do(); //42
ChildB.Do(); //12
Console.ReadKey();
}
}
Это не так уж и хорошо, поскольку вы можете наследовать только от одного абстрактного класса, и он попросит вас проявить снисходительность к вашей реализации new ().
Более того, я думаю, что это будет затратно с точки зрения памяти в зависимости от размера ваших унаследованных классов. Если у вас есть проблема с памятью, вам нужно будет установить все свойства / переменные после вашего нового в общедоступном методе, что является ужасным способом иметь значения по умолчанию.
Такой сценарий означает, что вам, вероятно, следует превратить это в объекты (не одиночные).