Почему я не могу объявить методы C # виртуальными и статическими? - PullRequest
47 голосов
/ 29 октября 2008

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

Есть ли способ обойти это? Я думаю, я мог бы использовать синглтон. HelperClass.Instance.HelperMethod () не намного хуже, чем HelperClass.HelperMethod (). Домовой указывает на любого, кто может указать на некоторые языки, которые поддерживают виртуальные статические методы.

Редактировать: ОК, да, я сумасшедший. Результаты поиска в Google заставили меня подумать, что меня там не было.

Ответы [ 14 ]

42 голосов
/ 29 октября 2008

Виртуальные статические методы не имеют смысла. Если я вызываю HelperClass.HelperMethod();, почему я ожидал, что будет вызван метод случайного подкласса? Решение действительно выходит из строя, когда у вас есть 2 подкласса HelperClass - какой из них вы бы использовали?

Если вы хотите иметь переопределяемые методы статического типа, вы, вероятно, должны использовать:

  • Синглтон, если вы хотите, чтобы тот же подкласс использовался глобально.
  • Традиционная иерархия классов с внедрением фабрики или зависимости, если вы хотите различного поведения в разных частях вашего приложения.

Выберите любое решение, которое имеет больше смысла в вашей ситуации.

32 голосов
/ 11 марта 2010

Я не думаю, что ты сумасшедший. Вы просто хотите использовать то, что в настоящее время невозможно в .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);
    }
15 голосов
/ 29 октября 2008

Действительно, это можно сделать в 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 и т. Д. Были параметром, и я мог вызвать соответствующий конструктор, используя фактический аргумент метакласса.

Вид фабричного образца бедняка:)

11 голосов
/ 23 августа 2012

Вы можете добиться того же эффекта, просто используя обычный статический метод, а затем затеняя его с помощью ключевого слова 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
8 голосов
/ 06 июля 2010

Я родом из 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);
        }
    } 

будет производить: корень полученный

7 голосов
/ 29 октября 2008

статический метод существует вне экземпляра класса. Он не может использовать любые нестатические данные.

виртуальный метод будет «перезаписан» перегруженной функцией в зависимости от типа экземпляра.

так что у вас есть четкое противоречие между статическим и виртуальным.

Это не проблема поддержки, это концепция.

Обновление: Здесь я ошибся (см. Комментарии):

Так что я сомневаюсь, что вы найдете любой ООП-язык, который будет поддерживать виртуальный статические методы.

6 голосов
/ 30 октября 2008

Вы не сумасшедший. То, на что вы ссылаетесь, называется поздним статическим связыванием; это было недавно добавлено в PHP. Есть отличная ветка, которая описывает это - здесь: Когда вам нужно использовать позднюю статическую привязку?

2 голосов
/ 30 мая 2011

Март правильно понял с ключевым словом "новый". Я на самом деле попал сюда, потому что мне нужен был такой тип функциональности, и решение Март прекрасно работает. Фактически, я взял его лучше и сделал метод базового класса абстрактным, чтобы программист предоставил это поле.

Мой сценарий был следующим:

У меня есть базовый класс 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) цена была доступна только через ТИП и (2) была предоставлена ​​цена.

1 голос
/ 14 ноября 2016

Существует способ принудительного наследования "абстрактных статических" методов от абстрактного обобщенного класса. Смотрите как следует:

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 ().

Более того, я думаю, что это будет дорогостоящим с точки зрения памяти, в зависимости от размера ваших унаследованных классов. В случае, если у вас есть проблема с памятью, вы должны будете установить все свойства / переменные после вашего нового в публичном методе, который является ужасным способом иметь значения по умолчанию.

1 голос
/ 18 сентября 2013

Метод переопределения предоставляет новую реализацию члена, унаследованного от базового класса. Метод, который переопределяется объявлением переопределения, называется переопределенным базовым методом. Переопределенный базовый метод должен иметь ту же сигнатуру, что и переопределенный метод. Вы не можете переопределить не виртуальный или статический метод. Переопределенный базовый метод должен быть виртуальным, абстрактным или переопределенным.

Объявление переопределения не может изменить доступность виртуального метода. И метод переопределения, и виртуальный метод должны иметь одинаковый модификатор уровня доступа.

Вы не можете использовать новые, статические или виртуальные модификаторы для изменения метода переопределения.

Объявление переопределяющего свойства должно указывать точно такой же модификатор доступа, тип и имя, что и унаследованное свойство, а переопределенное свойство должно быть виртуальным, абстрактным или переопределенным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...