Почему C# допускает абстрактный класс без абстрактных членов?

Спецификация C#, раздел 10.1.1.1, гласит:

Абстрактному классу разрешено (но не обязательно) содержать абстрактные члены.

Это позволяет мне создавать такие классы:

public abstract class A
{
    public void Main() 
    {
        // it's full of logic!
    }
}

Или еще лучше:

public abstract class A
{
    public virtual void Main() { }
}

public abstract class B : A
{
    public override sealed void Main()
    {
        // it's full of logic!
    }
}

Это действительно конкретный класс; он абстрактен только в той мере, в какой его нельзя создать. Например, если бы я хотел выполнить логику в B.Main(), мне пришлось бы сначала получить экземпляр B, что невозможно.

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

Иными словами, почему C# допускает абстрактный класс только с конкретными членами?

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


person Justin R.    schedule 08.06.2010    source источник
comment
Это действительно конкретный класс. Разве это ерунда?   -  person Rainning    schedule 09.11.2020


Ответы (9)


Возможно, хорошим примером является общий базовый класс, который предоставляет общие свойства и, возможно, другие члены для производных классов, но не представляет конкретный объект. Например:

public abstract class Pet
{
    public string Name{get;set;}
}

public class Dog : Pet
{
    public void Bark(){ ... }
}

У всех домашних животных есть имена, но само домашнее животное — это абстрактное понятие. Экземпляром домашнего животного должна быть собака или какое-либо другое животное.

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

person Michael Petito    schedule 08.06.2010
comment
@petito: очень хорошо объяснил. Спасибо. Мы могли бы сказать, что просто вернемся назад и посмотрим на наш дизайн и попытаемся определить (или составить) абстрактные концепции и построить «абстрактные» классы и посмотреть, как они могут измениться и принести пользу нашей модульности. - person Ramiz Uddin; 01.04.2011
comment
Хорошая точка зрения. Короче говоря, этот базовый класс Pet в данном случае концептуально является абстрактным классом, хотя технически он им и не обязан быть. Поэтому мы решили определить его как абстрактный класс. - person RayLuo; 10.04.2017

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

Например:

public abstract class FourLeggedAnimal
{

    public void Walk()
    {
        // most 4 legged animals walk the same (silly example, but it works)
    }

    public void Chew()
    {

    }
}

public class Dog : FourLeggedAnimal
{
    public void Bark()
    {
    }
}

public class Cat : FourLeggedAnimal
{
    public void Purr()
    {
    }
}
person Mike Marshall    schedule 08.06.2010
comment
Одним из ярких примеров является System.Windows.Forms.ButtonBase, от которого происходят Button, CheckBox и RadioButton. У него нет абстрактных членов, но много виртуальных методов и свойств. - person zinglon; 08.06.2010
comment
Это вызывает вопрос. Если, как вы говорите, идея состоит в том, чтобы заставить разработчика отказаться от класса, поскольку он предназначен только для обеспечения основы для предположительно более специализированной реализации, то почему бы и нет? Это вопрос, который я получаю в своем ОП. - person Justin R.; 08.06.2010
comment
Не уверен, в чем вопрос. Это действительно заставляет разработчика делать именно это, потому что (в моем примере) вызов конструктора (например, new FourLeggedAnimal() ) вызовет ошибку компилятора. - person Mike Marshall; 08.06.2010
comment
Я думаю, что ваш встроенный комментарий объясняет, почему это не отвечает на вопрос. САМЫЕ четвероногие животные... Я ожидаю, что в этом случае «Ходить и жевать» будет виртуальным. - person Michael Petito; 08.06.2010
comment
@zinglon: легко понять, почему у вас может быть абстрактный класс только с виртуальными методами, поскольку производные классы могут специализировать реализацию. Рассмотрим абстрактный класс посетителей только с виртуальными методами. Вопрос Fatcat: когда вы когда-либо определяли абстрактный класс, который не позволяет разработчику предоставлять специализированную реализацию? - person Michael Petito; 08.06.2010
comment
@mjmarsh: я думаю, что смысл толстяка в том, что вы можете создать новый класс, который абсолютно ничего не добавляет, а затем может быть создан экземпляр, а это означает, что создание абстрактного родительского класса бессмысленно. - person nlawalker; 08.06.2010
comment
@nlawalker @Michael: я знаю, что здесь мы начинаем мудрить, но могут быть случаи, когда разработчик абстрактного класса не знает, какие специализированные реализации сервисов захотят добавить, и поэтому не может добавлять какие-либо виртуальные методы/свойства, которые могут быть использования для производных классов. Я не уверен, что пример ButtonBase @zinglon соответствует этим критериям, но я думаю, что это допустимый вариант использования. - person Mike Marshall; 08.06.2010
comment
Я думаю, что такие абстрактные классы будут полезны только в том случае, если они состоят из свойств и имеют методы, которые воздействуют на эти свойства. В противном случае, как методы экземпляра абстрактного класса использовали бы экземпляр класса? У него нет состояния для доступа или изменения, и разработчик не может переопределить их, чтобы использовать состояние производного экземпляра. Разве тогда они не будут так же хороши, как статические? - person Michael Petito; 08.06.2010
comment
@fatcat1111: см. мой ответ о шаблоне Layer Supertype. - person Charles; 08.06.2010
comment
@mjmarsh: Чем это будет отличаться от создания конструктора protected без использования ключевого слова abstract? - person Jeff Yates; 08.06.2010
comment
@Джефф, пожалуйста, ознакомьтесь с моим комментарием к вопросу в ответ Майклу. Вы можете подорвать видимость с помощью динамических методов и даже Reflection.Emit, если отключите проверки видимости. Однако вы не можете ниспровергнуть реферат. - person Andras Zoltan; 08.06.2010

Я думаю, что несколько более точным представлением вашего вопроса было бы: почему C# допускает абстрактный класс с только конкретными членами?

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

person nlawalker    schedule 08.06.2010
comment
+1. Разработчики языка решили разрешить что-то вместо того, чтобы что-то ограничивать, потому что они, возможно, не смогут продумать все варианты использования, о которых когда-нибудь могут подумать их пользователи. - person tster; 09.06.2010

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

Это не «на самом деле конкретный класс», если вы объявляете его абстрактным. Это доступно вам в качестве выбора дизайна.

Этот выбор дизайна может быть связан с созданием сущностей, которые (с риском смешения терминологии) являются абстракциями объектов реального мира и удобочитаемостью. Вы можете захотеть объявить параметры типа Car, но не хотите, чтобы объекты объявлялись как Car — вы хотите, чтобы каждый объект типа Car создавался как Truck, Sedan, Coupe или Roadster. Тот факт, что Car не требует, чтобы наследники добавляли реализацию, не умаляет его значения как абстрактной версии своих наследников, которая сама не может быть реализована.

person Jay    schedule 08.06.2010

Абстрактное означает обеспечение абстракции поведения. Например, Автомобиль — это абстрактная форма. У него нет реального экземпляра, но мы можем сказать, что у Vehicle есть ускорение. Точнее, Ford Ikon — это транспортное средство, а Yamaha FZ — это транспортное средство. Оба они имеют ускоряющее поведение.

Если вы сейчас сделаете это в форме класса. Транспортное средство — это абстрактный класс с методом ускорения. Хотя вы можете/не можете предоставить какой-либо абстрактный метод. Но бизнес-потребность заключается в том, что Vehicle не должен создавать экземпляр. Следовательно, вы делаете его абстрактным. Два других класса — Ikon и FZ — это конкретные классы, производные от класса Vehicle. Эти два будут иметь свои собственные свойства и поведение.

person Chinjoo    schedule 08.06.2010

Что касается использования, использование abstract в объявлении класса, но без абстрактных членов, это то же самое, что наличие класса public, но использование protected в его конструкторах. Оба заставляют класс быть производным для его создания.

Однако, что касается самодокументирующегося кода, помечая класс abstract, он информирует других о том, что этот класс никогда не предназначен для создания экземпляров сам по себе, даже если он не имеет членов virtual или abstract. В то время как защита конструкторов не делает такого утверждения.

person Jeff Yates    schedule 08.06.2010
comment
По крайней мере, в vb.net защищенные конструкторы могут быть не только связаны с конструкторами производного класса, но также могут вызываться из кода производного класса для создания новых экземпляров типов этих конструкторов. - person supercat; 29.06.2012

Компилятор не мешает логике реализации, но в вашем случае я бы просто опустил abstract ?! Кстати, некоторые методы могли быть реализованы с помощью { throw Exception("must inherit"); }, и компилятор не смог различить полностью реализованные классы и функции, включая только throw.

person ralf.w.    schedule 08.06.2010
comment
Те методы, которые вызывают только исключения, обычно должны быть абстрактными, а если нет, то они должны быть, по крайней мере, виртуальными! - person Michael Petito; 08.06.2010

Вот возможная причина:

Супертип слоя

Нередко все объекты слоя имеют методы, которые нежелательно дублировать в системе. Вы можете переместить все это поведение в общий супертип слоя.

-- Мартин Фаулер

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

person Charles    schedule 08.06.2010
comment
Отличается ли это от переноса методов из общего супертипа в статический класс? - person Michael Petito; 08.06.2010
comment
Я думаю, что если бы я объединил общие методы, я бы предоставил их либо в статическом классе, либо в базовом классе. Однако это не относится к моему вопросу: зачем разрешать абстрактный класс без каких-либо абстрактных членов? - person Justin R.; 09.06.2010
comment
…правильно, потому что нет причин, по которым супертип слоя должен быть объявлен абстрактным. - person Jay; 09.06.2010

Я вижу абстрактные классы, служащие двум основным целям:

  • Неполный класс, который должен быть специализирован для предоставления какой-либо конкретной услуги. Здесь абстрактные члены будут необязательными. Класс будет предоставлять некоторые услуги, которые могут использовать дочерние классы, и может определять абстрактные члены, которые он использует для предоставления своих услуг, как в Шаблон метода шаблона. Этот тип абстрактного класса предназначен для создания иерархии наследования.

  • Класс, предоставляющий только статические служебные методы. В этом случае абстрактные члены вообще не имеют смысла. C# поддерживает это понятие с помощью статических классов, они неявно абстрактны и запечатаны. Это также может быть достигнуто с помощью закрытого класса с частным конструктором.

person Jordão    schedule 08.06.2010