C #, реализовать «статические абстрактные» методы - PullRequest
39 голосов
/ 05 мая 2009

Недавно я столкнулся с проблемой, когда мне кажется, что мне нужен метод «статического абстрактного». Я знаю, почему это невозможно, но как мне обойти это ограничение?

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

abstract class AbstractBase
{
    ...
    public static abstract string Description{get;}
    ...
}

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

Должен ли я сделать его просто нестатичным и всегда получать экземпляр для получения информации, специфичной для этого класса?

Есть идеи?

Ответы [ 7 ]

31 голосов
/ 05 мая 2009

Вы не можете.

Место для этого - Атрибуты.

Например

[Name("FooClass")]
class Foo
{
}
6 голосов
/ 11 мая 2009

Если вы не возражаете отложить реализацию, чтобы разумно реализовать свойство Description, вы можете просто сделать

public abstract string ClassDescription {get; } 
// ClassDescription is more intention-revealing than Description

И реализация классов будет делать что-то вроде этого:

static string classDescription="My Description for this class";
override string  ClassDescription { get { return classDescription; } }

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

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

5 голосов
/ 05 мая 2009

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

Почему бы не просто классическим способом?

abstract class AbstractBase
{
    protected string _Description = "I am boring abstract default value";
}

class Foo : AbstractBase {

     public Foo() {
       _Description = "I am foo!";
     }
}
5 голосов
/ 05 мая 2009

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

Я могу понять, почему вам нужна такая комбинация, но факт - единственный эффект, заключающийся в том, чтобы отрицать использование реализации this или любых нестатических элементов. То есть родительский класс будет диктовать ограничение в реализации производного класса, даже если между вызовом абстрактного или «статического абстрактного» члена нет принципиальной разницы (поскольку для обоих потребуется конкретный экземпляр, чтобы выяснить, какую реализацию использовать)

3 голосов
/ 05 мая 2009

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

Если вы не вызываете его в экземпляре, то в игре нет полиморфизма (т. Е. ChildA.Description полностью не связано с ChildB.Description в том, что касается языка).

2 голосов
/ 10 июня 2018

Возможный обходной путь - определить Singleton вашего производного класса в базовом классе с помощью Generics.

import System;

public abstract class AbstractBase<T>
    where T : AbstractBase<T>, new()
{
    private static T _instance = new T();

    public abstract string Description { get; }
    public static string GetDescription()
    {
        return _instance.Description;
    }
}

public class DerivedClass : AbstractBase<DerivedClass>
{
    public override string Description => "This is the derived Class";
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(DerivedClass.GetDescription());
        Console.ReadKey();
    }
}

Хитрость заключается в том, чтобы рассказать вашему AbstractBase<T> некоторые подробности о том, как DerivedClass реализовано:

  • Он обновляется с where T: new(), поэтому может создавать экземпляр Singleton
  • Он наследуется от where T : AbstractBase<T>, поэтому он знает, что будет реализация Description

Таким образом, _instance содержит поле Description, которое можно вызвать в статическом методе GetDescription(). Это заставляет вас перезаписывать Description в вашем DerivedClass и позволяет вам вызывать его значение с DerivedClass.GetDescription()

0 голосов
/ 09 апреля 2014

Вы можете сделать так, чтобы «абстрактный» базовый метод выдал Exception, поэтому разработчик будет «предупрежден», если попытается вызвать этот метод в дочернем классе без переопределения.

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

...