В чем разница между абстрактным классом и классом только с защищенными конструкторами? (.СЕТЬ)

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

РЕДАКТИРОВАТЬ:

Как бы вы создали экземпляр в производном классе с базовым классом с защищенным конструктором? Например:

public class ProtectedConstructor
{
    protected ProtectedConstructor()
    {

    }

    public static ProtectedConstructor GetInstance()
    {
        return new ProtectedConstructor(); // this is fine
    }
}

public class DerivedClass : ProtectedConstructor
{

    public void createInstance()
    {
        ProtectedConstructor p = new ProtectedConstructor(); // doesn't compile
    }

    public static ProtectedConstructor getInstance()
    {
        return new ProtectedConstructor(); // doesn't compile

    }
}

person Community    schedule 05.02.2010    source источник
comment
Почему эта вики отмечена флажком? Здесь есть внятные, четкие ответы...   -  person Reed Copsey    schedule 05.02.2010
comment
Я отредактировал свой ответ, чтобы добавить предложение для этого.   -  person Ray    schedule 05.02.2010
comment
@ Рид Извини, моя вина. Я был обеспокоен тем, что это было слишком открытым.   -  person David Hodgson    schedule 06.02.2010


Ответы (9)


Вы можете создать экземпляр класса с защищенными конструкторами внутри самого класса — в статическом конструкторе или статическом методе. Это можно использовать для реализации синглтона или фабричного типа.

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

Редактировать:

если вы вызываете ProtectedConstructor.GetInstance(); вместо new ProtectedConstructor();, это работает. Может быть, защищенные конструкторы нельзя так называть? Но защищенные методы, безусловно, могут.

Вот интересная статья на эту тему.

person Community    schedule 05.02.2010
comment
Вы также можете создать экземпляр класса из подкласса или с помощью отражения. - person Reed Copsey; 05.02.2010
comment
Верно, но в таком случае было бы разумнее пометить их как частные. - person Jonathan Allen; 05.02.2010
comment
Но в этом случае base() не будет работать в производном классе. Пометка их как частных или защищенных приводит к другому поведению. - person Brian; 06.02.2010

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

Однако пометка класса abstract имеет два преимущества:

  1. С защищенными конструкторами по-прежнему можно создать экземпляр класса двумя способами. Вы можете использовать Activator.CreateInstance с BindingFlags.NonPublic или используйте фабричный метод, определенный в классе (или подклассе), чтобы создать экземпляр класса. Однако класс, помеченный как абстрактный, создать нельзя.

  2. Вы делаете свое намерение более ясным, помечая класс abstract. Лично я считаю, что это самая веская причина для этого.

person Community    schedule 05.02.2010

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

person Community    schedule 05.02.2010
comment
Как бы вы поступили от наследника? Я отредактировал вопрос выше. - person David Hodgson; 05.02.2010
comment
Защищенный конструктор может быть вызван только конструктором унаследованного класса (т.е. public DerivedClass() : base() будет работать), а не из методов в унаследованном классе. Я не уверен в рассуждениях. - person technophile; 05.02.2010

Абстрактный класс может иметь абстрактные методы; методы, состоящие только из сигнатуры метода, но без тела, которые должны реализовывать дочерние классы.

Серьезно, никто еще не упомянул об этом?

person Community    schedule 05.02.2010

Ваш пример ошибочен, потому что в случае с getInstance вы создаете класс ProtectedConstructor и ожидаете, что он будет преобразован в DerivedClass. Вместо этого вам нужна более полная реализация, в которой производный класс имеет конструктор:

public class ProtectedConstructor
{
    protected ProtectedConstructor(string arg)
    {
        // do something with arg
    }

    public static ProtectedConstructor GetInstance()
    {
        return new ProtectedConstructor("test"); 
    }
} 

public class DerivedClass : ProtectedConstructor
{
    protected DerivedClass(string arg) : base(arg)
    {
    }

    public void createInstance()
    {
        DerivedClass p = new DerivedClass("test"); 
    }

    public static DerivedClass getInstance()
    {
        return new DerivedClass("test"); 
    }
}

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

public abstract Thread
{
    protected Thread()
    {
    }

    public void Run()
    {
        LogStart();
        DoRun();
        LogEnd();
    }

    protected abstract DoRun();

    private void LogStart()
    {
         Console.Write("Starting Thread Run");
    }

    private void LogEnd()
    {
         Console.Write("Ending Thread Run");
    }
}


public class HelloWorldThread : Thread
{
    public HelloWorldThread()
    {
    }

    protected override DoRun()
    {
        Console.Write("Hello World");
    }
}
person Community    schedule 05.02.2010
comment
@Jon Извини, я не хотел, чтобы это было удручено; Я отредактировал вопрос. Я пытался сказать, что вы не можете создать экземпляр ProtectedConstructor даже в производном классе. - person David Hodgson; 06.02.2010

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

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

person Community    schedule 05.02.2010

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

Типичным примером этого может быть что-то вроде шаблона Singleton: http://en.wikipedia.org/wiki/Singleton_pattern

person Community    schedule 05.02.2010

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


public abstract class Parent
{
  protected abstract void AMethod();
}

public abstract class Child: Parent
{
  // does not implement AMethod, and that's ok
}

public class Child2: Parent
{
  // does not implement AMethod, and that will cause a compile error
}

person Community    schedule 05.02.2010

Если вы намерены разрешить только статическое использование класса (т. е. не использовать его как чистый базовый класс), вам следует вместо этого использовать ключевое слово static; CLR предотвратит создание экземпляров класса любым методом, включая Reflection (AFAIK).

person Community    schedule 05.02.2010