抽象类和只有受保护构造函数的类有什么区别?(.NET)

9

抽象类和只有受保护构造函数的类之间有什么区别?它们看起来非常相似,因为你都不能实例化它们。

编辑:

如果基类有一个受保护的构造函数,如何在派生类中创建一个实例?例如:

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

    }
}

4
为什么这篇文章被标记为维基百科?这里有明确清晰的答案。 - Reed Copsey
我编辑了我的答案,为此添加了一个建议。 - Ray
@Reed 对不起,是我的错。我担心这个问题太过开放了。 - David Hodgson
9个回答

12

你可以在类本身内使用静态构造函数或静态方法实例化具有受保护构造函数的类。这可以用于实现单例模式或工厂模式。

抽象类根本无法实例化 - 这意味着一个或多个子类将完善实现,并且那些子类将被实例化。

编辑:

如果您调用 ProtectedConstructor.GetInstance(); 而不是 new ProtectedConstructor();,它会成功。也许protected构造函数不能以这种方式调用?但是protected方法肯定可以。

这篇文章 是关于此主题的一个有趣的文章。


1
您还可以从子类或通过反射实例化该类。 - Reed Copsey
真的,但在这种情况下,将它们标记为私有可能更有意义。 - Jonathan Allen
但在这种情况下,base() 在派生类中就无法使用了。将它们标记为 private 或 protected 会导致不同的行为。 - Brian

3
大多数情况下,实际上没有太大的区别,因为两者都只能通过子类生成。
然而,将一个类标记为abstract有两个好处:
  1. 使用受保护的构造函数,仍然可以通过两种方式创建类的实例。您可以使用Activator.CreateInstance和BindingFlags.NonPublic,或者您可以使用在类(或子类)中定义的工厂方法来创建类的实例。但是,标记为抽象的类无法被创建。

  2. 通过将类标记为abstract,您可以更清楚地表达您的意图。就我个人而言,这是最具说服力的原因。


1

你的例子有缺陷,因为在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");
    }
}

@Jon 抱歉,我不是有意贬低;我已经编辑了问题。我的意思是,即使在派生类中也无法实例化 ProtectedConstructor。 - David Hodgson

1

从外部黑盒的角度来看,它们是相似的,因为你都不能实例化它们。然而,你永远无法实例化一个抽象类,但你可以从类本身或继承者中使用受保护的构造函数构造一个类。


你会如何从子类中继承实现它?我已经编辑了上面的问题。 - David Hodgson
1
受保护的构造函数只能被继承类的构造函数调用(即public DerivedClass():base()将起作用),而不能从继承类中的方法中调用。我不确定其原因。 - technophile

1

抽象类可以拥有抽象方法;这些方法只包含方法签名,但没有具体实现,子类必须实现。

说真的,还没有一个人提到这个吗?


1
另一个需要考虑的事情是,我没有看到其他人提到的,就是你的代码可能会在将来被维护。如果维护者给一个类添加了公共构造函数,那么它就可以实例化。这可能会破坏你的设计,所以你应该防止这种情况发生(或者设计时考虑适应这种情况)。
为了防止其他人进行这些更改,你可以在代码中加注释。或者,就像其他人说的那样,使用"abstract"关键字来明确说明你的意图。

0
首先,最容易想到的差異是抽象類別無法實例化,但具有受保護建構函式的類別可以通過其他公共方法來實例化。
一個常見的例子可能是單例模式:http://en.wikipedia.org/wiki/Singleton_pattern

0

如果您从另一个抽象类继承抽象类,则不必满足抽象方法,但是对于具有受保护构造函数的普通类,则必须这样做。 例如:


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

public abstract class Child: Parent
{
  // 不实现AMethod,这没问题
}

public class Child2: Parent
{
  // 不实现AMethod,这将导致编译错误
}


0
如果您的意图仅允许类的静态使用(即不将其用作纯基类),则应改用 static 关键字;CLR 将通过任何方法(包括 Reflection)防止创建类的实例(据我所知)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接