C# 什么';抽象类和只有受保护构造函数的类之间的区别是什么?(.NET)

C# 什么';抽象类和只有受保护构造函数的类之间的区别是什么?(.NET),c#,constructor,abstract-class,protected,C#,Constructor,Abstract Class,Protected,抽象类和仅具有受保护构造函数的类之间有什么区别?它们似乎与我非常相似,因为你不能实例化任何一个 编辑: 如何在派生类中创建一个具有受保护构造函数的基类的实例?例如: public class ProtectedConstructor { protected ProtectedConstructor() { } public static ProtectedConstructor GetInstance() { return new Pr

抽象类和仅具有受保护构造函数的类之间有什么区别?它们似乎与我非常相似,因为你不能实例化任何一个

编辑:

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

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

    }
}

可以在类本身中(在静态构造函数或静态方法中)实例化具有受保护构造函数的类。这可以用来实现单例或工厂类型的东西

抽象类根本不能被实例化——其目的是一个或多个子类将完成实现,这些类将被实例化

编辑:

如果调用
ProtectedConstructor.GetInstance()而不是新的ProtectedConstructor(),它可以工作。也许受保护的构造函数不能这样调用?但受保护的方法当然可以


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

从外部的黑盒子的角度来看,是的,它们是相似的,你不能实例化任何一个。但是,您可以从不实例化抽象类,在抽象类中,您可以仅从类本身或继承器中使用受保护的构造函数来构造类。

大多数情况下,实际上没有什么区别,因为两者都只能通过子类生成

但是,标记类
摘要
有两个好处:

  • 使用受保护的构造函数,仍然可以通过两种方式创建类的实例。您可以与BindingFlags.NonPublic一起使用,也可以使用类(或子类)中定义的工厂方法来创建类的实例。但是,无法创建标记为abstract的类

  • 通过标记类
    摘要
    ,您可以更清楚地表达您的意图。就我个人而言,我认为这是这样做的最令人信服的理由


  • 首先想到的区别是抽象类不能被实例化,但是一个具有受保护构造函数的类可以通过另一个公共方法被实例化


    这方面的一个常见示例可能类似于单例模式:

    如果您从另一个抽象类继承一个抽象类,则不必满足抽象方法,但可以使用具有受保护的构造函数的普通类。例子

    
    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
    }
    

    抽象类可以有抽象方法;只包含子类必须实现的方法签名而不包含主体的方法


    说真的,还没有人提到过这一点?

    如果您的意图是只允许静态使用该类(即不将其用作纯基类),那么您应该使用static关键字;CLR将阻止通过包括反射(AFAIK)在内的任何方法创建类的实例。

    您的示例存在缺陷,因为在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"); 
        }
    }
    
    不管抽象类的主要区别是什么,抽象类的用法是定义子类必须实现的抽象方法,但您不想为其提供默认实现。例如,假设您有一个线程类,它有一个Run方法。您希望确保每个运行调用首先设置一些日志记录,然后执行线程的实际工作,然后停止日志记录。您可以编写如下抽象线程类:

    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");
        }
    }
    

    另一个需要考虑的事情是,我没有看到其他人提到的是,你的代码可能会在将来被维护。如果维护人员向类添加公共构造函数,则可以对其进行实例化。这可能会破坏您的设计,因此您应该阻止它(或设计以适应它)


    为了防止其他人进行此类更改,您可以对代码进行注释。或者,正如其他人所说,使用“摘要”来明确记录您的意图。

    为什么要标记此Wiki?这里有明确的答案……我编辑了我的答案,为这个添加了一个建议。@Reed抱歉,是我的错。我担心它太开放了。你也可以从一个子类实例化这个类,或者通过反射来实例化。是的,但是在这种情况下,将它们标记为私有会更有意义。但是在这种情况下,base()在派生类中不起作用。将它们标记为私有或受保护会导致不同的行为。您将如何从继承者处执行此操作?我已经编辑了上面的问题。受保护的构造函数只能由继承类的构造函数调用(即public DerivedClass():base()将起作用),而不能从继承类中的方法调用。“我不知道原因是什么。”乔恩:对不起,我不是有意要让人失望的;我已经编辑了这个问题。我想说的是,即使在派生类中,也不能实例化ProtectedConstructor。