C#, реализуйте «статические абстрактные» методы

StackOverflow https://stackoverflow.com/questions/823665

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Есть идеи?

Это было полезно?

Решение

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

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

Другие советы

Ты не можешь.

Это можно сделать с помощью атрибутов.

Например

[Name("FooClass")]
class Foo
{
}

Если вы не возражаете отложить реализацию, чтобы разумно реализовать свойство 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; } }

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

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

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

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

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

class Foo : AbstractBase {

     public Foo() {
       _Description = "I am foo!";
     }
}

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top