Подтвердить что ты не робот

Абстрактный метод в нелинейном классе

Я хочу знать причину проектирования ограничения абстрактных методов в нечерном классе (в С#).

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

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

4b9b3361

Ответ 1

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

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

Теперь я попытаюсь быть более конкретным, используя пример: представьте следующий код:

Animal[] zoo = new Animal[] { new Monkey(), new Fish(), new Animal() };

foreach (Animal animal in zoo)
    animal.MakeSound();

Здесь Animal - это базовый класс non abstract (именно поэтому я могу поместить его непосредственно в массив), Monkey и Fish получены из Animal, а MakeSound() - это abstract. Что должен делать этот код? Вы не указали это четко, но я могу представить несколько вариантов:

  • Вы не можете вызывать MakeSound() для переменной, напечатанной как Animal, вы можете вызывать ее только с использованием переменной, набираемой как один из производных классов, поэтому это ошибка компиляции.

    Это нехорошее решение, потому что вся точка abstract должна иметь возможность рассматривать экземпляры производных классов как базовый класс и по-прежнему получать поведение, специфичное для производного класса. Если вы этого хотите, просто поместите обычный (no abstract, virtual или override) метод в каждый производный класс и ничего не делайте с базовым классом.

  • Вы не можете вызвать MakeSound() для объекта, тип выполнения которого на самом деле Animal, поэтому это ошибка времени выполнения (исключение).

    Это тоже нехорошее решение. С# - это статически типизированный язык, поэтому он пытается уловить ошибки, такие как "вы не можете вызвать этот метод" во время компиляции (с очевидными исключениями, такими как отражение и dynamic), поэтому включение этого в ошибку времени выполнения не соответствовало бы остальная часть языка. Кроме того, вы можете сделать это легко, создав метод virtual в базовом классе, который генерирует исключение.

Подводя итог, вы хотите что-то, что не имеет особого смысла, пахнет плохим дизайном (базовый класс, который ведет себя иначе, чем его производные классы), и его можно легко обрабатывать. Это все поет функции, которая не должна быть реализована.

Ответ 2

Итак, вы хотите разрешить

class C { abstract void M(); }

для компиляции. Предположим, что так оно и было. Что вам тогда нужно делать, когда кто-то делает

new C().M();

? Вам нужна ошибка выполнения? Ну, в общем случае С# предпочитает ошибки времени компиляции для ошибок времени выполнения. Если вам не нравится эта философия, есть другие доступные языки...

Ответ 3

Я думаю, что вы ответили на свой вопрос, абстрактный метод не определяется изначально. Поэтому класс не может быть обусловлен. Вы говорите, что это следует игнорировать, но по определению при добавлении абстрактного метода вы говорите: "Каждый класс, созданный из этого, должен реализовать этот {абстрактный метод}", следовательно, класс, в котором вы определяете абстрактный класс, также должен быть абстрактным, поскольку абстрактный метод все еще undefined в этой точке.

Ответ 4

Вы можете достичь того, что хотите использовать "виртуальные" методы, но использование виртуальных методов может привести к большим ошибкам бизнес-логики во время выполнения, поскольку разработка не "вынуждена" реализовать логику в дочернем классе.

Я думаю, здесь есть верный момент. Абстрактным методом является идеальное решение, так как оно "принудительно" требует определения тела метода у детей.

Я столкнулся со многими ситуациями, когда родительскому классу пришлось (или было бы более эффективно) реализовать некоторую логику, но дети "Только" могли реализовать остальную часть логики "

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

@AakashM, я ценю, что С# предпочитает ошибки времени компиляции. И я тоже. И так кто-то. Речь идет о мышлении из коробки.

И поддержка этого не повлияет на это.

Позвольте думать из коробки здесь, вместо того, чтобы говорить "ура" решениям большого мальчика.

Компилятор С# может обнаруживать и отказывать кому-либо в использовании абстрактного класса напрямую, потому что он использует ключевое слово abstract.

С# также знает, чтобы любой дочерний класс реализовал любые абстрактные методы. Как? из-за использования "абстрактного" ключевого слова.

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

Итак, почему С# не обнаруживает ключевое слово "abstract" рядом с методом в обычном классе и обрабатывает его в ВРЕМЯ COMPILE.

Причина в том, что требуется "переработка", и усилия не стоят поддержки малого спроса.

Специально в индустрии, в которой не хватает людей, которые думают о коробках, которые им дали большие мальчики.

Ответ 5

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

class MyConcreteClass
{
  readonly Func<int, DateTime, string> methodImpl;

  // constructor requires a delegate instance
  public MyConcreteClass(Func<int, DateTime, string> methodImpl)
  {
    if (methodImpl == null)
      throw new ArgumentNullException();

    this.methodImpl = methodImpl;
  }

  ...
}

(Подпись string MethodImpl(int, DateTime) - это просто пример, конечно.)

В противном случае я могу порекомендовать другие ответы, чтобы объяснить, почему ваше желание, вероятно, не является чем-то, что сделало бы мир лучше.

Ответ 6

Таким образом, ответы выше верны: наличие абстрактных методов делает класс неотъемлемым абстрактным. Если вы не можете использовать экземпляр класса, то вы не можете использовать экземпляр класса. Тем не менее, ответы выше не обсуждали ваши варианты здесь.

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

В качестве альтернативы вы можете сохранить эти методы в классе, но затем вместо абстрактных методов объявите интерфейс. По сути, у вас есть проблема с множественным наследованием, так как вы хотите, чтобы производный класс наследовал от двух концептуально разных объектов: не абстрактного родителя с публичными статическими членами и абстрактного родителя с абстрактными методами. В отличие от некоторых других фреймворков, С# допускает множественное наследование. Вместо этого С# предлагает официальное объявление интерфейса, предназначенное для заполнения этой цели. Более того, вся суть абстрактных методов - это просто навязать определенный концептуальный интерфейс.

Ответ 7

У меня сценарий, очень похожий на то, что пытается достичь ОП. В моем случае метод, который я хочу сделать абстрактным, будет защищенным методом и будет известен только базовому классу. Итак, "новый C().M();" не применяется, поскольку данный метод не является общедоступным. Я хочу иметь возможность создавать экземпляры и вызывать общедоступные методы в базовом классе (поэтому он должен быть не абстрактным), но мне нужны эти общедоступные методы для вызова защищенной реализации защищенного метода в дочернем классе и не имеют реализации по умолчанию в родительском. Говоря языком, мне нужно заставить потомков переопределить метод. Я не знаю, что дочерний класс находится во время компиляции из-за инъекции зависимостей.

Мое решение состояло в том, чтобы следовать правилам и использовать конкретный базовый класс и виртуальный защищенный метод. Однако для реализации по умолчанию я бросаю NotImplementedException с ошибкой "Реализация имени метода должна предоставляться в реализации дочернего класса".

protected virtual void MyProtectedMethod() 
{ 
  throw new NotImplementedException("The implementation for MyProtectedMethod must be provided in the implementation of the child class."); 
}

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