【问题标题】:Constructor of an abstract class in C#C#中抽象类的构造函数
【发布时间】:2011-08-01 21:17:51
【问题描述】:

为什么可以在 C# 中为抽象类编写构造函数?
据我所知,我们不能实例化一个抽象类。那它有什么用呢?
你不能实例化这个类,对吧?

【问题讨论】:

    标签: c# constructor abstract-class


    【解决方案1】:

    因为您可能希望在抽象类中实例化数据的标准方式。这样,您就可以让从该类继承的类调用基构造函数。

    public abstract class A{
    
        private string data;
    
        protected A(string myString){
          data = myString;
        }
    
    }
    
    public class B : A {
    
         B(string myString) : base(myString){}
    
    }
    

    【讨论】:

    • 但是抽象类被定义为一个我们不能实例化它的类。如果我用它来初始化一个抽象的基类上的只读变量,可以吗?
    • 是的,如果我理解正确的话。您不能实例化抽象类本身,但派生类仍然具有基类的所有部分。这样做不仅有效,而且是您这样做的主要原因之一。
    • 好的,现在如果我有一个从其他抽象类(第二级)继承的第一级抽象类和第三类(非抽象类)从第二层抽象类继承,我该如何初始化一个在第三个普通类的 c'tor 内的第一层抽象类上声明的只读变量我需要向上两级直到 c'tor
    • 我真正能想到的唯一方法如下: public abstract class A { protected readonly string data;受保护的 A(string myData) { data = myData; } } 公共抽象类 B :A { 保护 B(string myData): base(myData){ } } 公共类 C : B { C(string myData): base(myData){ } }
    • 那么当你调用 new B() 时,它会调用父类的构造函数吗?
    【解决方案2】:

    您可以在实现所有方法后实例化它。然后会调用构造函数。

    【讨论】:

      【解决方案3】:

      这是一种强制执行抽象类的一组不变量的方法。也就是说,无论子类做什么,您都希望确保基类的某些事情总是正确的......示例:

      abstract class Foo
      {
          public DateTime TimeCreated {get; private set;}
      
          protected Foo()
          {
               this.TimeCreated = DateTime.Now;
          }
      }
      
      abstract class Bar : Foo
      {
          public Bar() : base() //Bar's constructor's must call Foo's parameterless constructor.
          { }
      }
      

      不要将构造函数视为new 运算符的对偶。构造函数的唯一目的是确保您在开始使用之前拥有处于有效状态的对象。恰好我们通常通过new运算符调用它。

      【讨论】:

      • 您不需要在派生类的构造函数上调用base()。它将被自动调用,因为那是无参数构造函数。这意味着你的构造函数等价于public Bar() {}
      • @RobertKoritnik - 这很奇怪,因为我无法回忆甚至想象我不知道的时候,但它就在那里。
      • 旁注/评论;我喜欢你使用这些词的事实;奇数和偶数在同一个句子中。
      【解决方案4】:

      它可以强制执行抽象类的所有实现或您在抽象类上实现的任何方法所需的一些初始化逻辑(并非抽象类上的所有方法都必须是抽象的,有些可以实现)。

      从抽象基类继承的任何类都必须调用基构造函数。

      【讨论】:

        【解决方案5】:

        据我所知,我们无法实例化抽象类

        你的错误就在那里。 当然你可以实例化一个抽象类。

        abstract class Animal {}
        class Giraffe : Animal {}
        ...
        Animal animal = new Giraffe();
        

        那里有一个 Animal 的实例。您通过创建一个派生自抽象类的具体类并实例化它来实例化一个抽象类。请记住,派生具体类的实例也是其抽象基类的实例。 即使 Animal 是抽象的,Giraffe 的实例也是 Animal 的实例。

        鉴于您可以实例化一个抽象类,它需要像任何其他类一样具有构造函数,以确保满足其不变量。

        现在,static 类是您实际上无法实例化的类,您会注意到在静态类中创建实例构造函数是不合法的。

        【讨论】:

        • 从术语的角度来看,如果Foo 是一个可继承的具体类型,而DerivedFoo:Foo,你会用什么术语来描述一个堆对象实例,它将其类型报告为Foo,如不同于DerivedFoo?我自己的倾向是使用短语“T 的实例”来指代 exact 类型为 T 的东西。否则我会倾向于说“T 或某种派生”之类的话, “T 的一些派生”、“T 的一些实现”或“满足 T 的东西”取决于 T 是具体类、抽象类、接口还是约束。
        • 使用该逻辑,您可以获得接口实例 :-)
        • @Eric msdn.microsoft.com/en-us/library/sf985hc5.aspx 说抽象类不能被实例化
        • @M.Babcock:静态 initializers 已运行,但没有创建该类型的 instance
        • @supercat 不幸的是,这是一种语义误解,会导致多态性的误用。长颈鹿“是”动物。 Giraffe 的实例也是 Animal 的实例,并且必须在需要 Animal 实例的每个地方都可用。埃里克在这里是绝对正确的。坚持 Giraffe 的实例不完全是 Animal 的实例可能会导致危险的反模式,例如允许 Rectangle 是 Square 的子类,即使它不保持相同的不变量并且不能严格替代。
        【解决方案6】:

        我也想在抽象的表面上发光 所有答案几乎涵盖了所有内容。还是我的 2 美分

        抽象类是普通类,有一些例外

        1. 该类的任何客户/消费者都无法创建该类的对象 类,它从不意味着它的构造函数永远不会调用。它的派生类可以选择调用哪个构造函数。(如某些答案所示)
        2. 它可能具有抽象功能。

        【讨论】:

          【解决方案7】:

          在可继承的具体类Thing 中定义具有公共或内部存储类的构造函数有效地定义了两种方法:

          • 作用于this的方法(我称之为InitializeThing)没有返回值,只能从ThingCreateThingInitializeThing方法调用,和子类的InitializeXXX 方法。

          • 一个方法(我称之为CreateThing),它返回一个构造函数指定类型的对象,本质上表现为:

            Thing CreateThing(int whatever)
            {
                Thing result = AllocateObject<Thing>();
                Thing.initializeThing(whatever);
            }
            

          抽象类有效地创建了仅第一种形式的方法。从概念上讲,上面描述的两个“方法”没有理由需要具有相同的访问说明符。然而,在实践中,没有办法以不同的方式指定它们的可访问性。请注意,就实际实现而言,至少在 .NET 中,CreateThing 并没有真正实现为可调用方法,而是表示插入到 newThing = new Thing(23); 语句中的代码序列。

          【讨论】:

            【解决方案8】:

            通常构造函数涉及初始化正在创建的对象的成员。在继承的概念中,通常继承层次结构中的每个类构造函数都负责实例化自己的成员变量。这是有道理的,因为必须在定义变量的地方进行实例化。

            由于抽象类不是完全抽象的(不像接口),它是抽象和具体成员的混合,非抽象的成员需要初始化,这是在抽象类的构造函数中完成的,有必要在抽象类中有构造函数。当然抽象类的构造函数只能从派生类的构造函数中调用。

            【讨论】:

              【解决方案9】:

              抽象类可以有成员变量需要初始化,所以可以在抽象类构造函数中初始化,在派生类对象初始化时调用该构造函数。

              【讨论】:

                【解决方案10】:

                有以下两个重要特性阻止继承抽象类

                1. 抽象类必须有抽象方法,否则不是抽象类

                2. 抽象类必须由派生类继承,所以如果一个类被其他类继承而不是用来创建该类的对象

                【讨论】:

                • 我相信你首先指出的是不正确的,很可能有一个没有任何抽象方法的抽象类。
                【解决方案11】:

                来自https://msdn.microsoft.com/en-us/library/ms182126.aspx

                抽象类型的构造函数只能由派生类型调用。因为公共构造函数创建类型的实例,而您不能创建抽象类型的实例,所以具有公共构造函数的抽象类型设计不正确。

                由于只有派生类可以使用抽象类构造函数,因此如果需要,必须将抽象类构造函数声明为protected

                但是,当在抽象类中声明公共构造函数时,所述 VS 编译器不会抱怨(使用默认规则),但它不允许创建新实例。

                【讨论】:

                  【解决方案12】:

                  你完全正确。我们无法实例化抽象类,因为抽象方法没有任何主体,即抽象方法无法实现。但是可能有一些场景需要初始化基类的一些变量。 您可以按照@Rodrick 的建议使用 base 关键字来做到这一点。在这种情况下,我们需要在抽象类中使用构造函数。

                  【讨论】:

                    【解决方案13】:

                    关于抽象类的要点

                    1. 无法实例化抽象类。
                    2. 抽象类可以有构造函数和析构函数。
                    3. 抽象类不能是密封类,因为密封 修饰符防止类被继承。
                    4. 抽象类包含抽象成员和非抽象成员。
                    5. 抽象类成员可以是私有的、受保护的和内部的。
                    6. 抽象成员不能有私有访问修饰符。
                    7. 抽象成员是隐式虚拟的,必须由 非抽象派生类。

                    【讨论】:

                      【解决方案14】:

                      添加到上面的答案和例子。

                      是的,一个抽象类可以有一个构造函数,即使一个抽象类不能被实例化。将解释一个抽象类构造函数 c# 代码示例。但是,下一个问题也可能出现,好像我们不能实例化(使用new构造对象)抽象类,那么构造函数在抽象类中是什么,或者为什么我们要在抽象类中使用构造函数?

                      请注意,当我们创建派生类的对象时,会隐式调用抽象基类的构造函数,即使我们无法实例化抽象类。比如在程序中,如果我们创建一个派生类的对象,那么抽象基类的构造函数也会被调用。

                      这也是例子之一

                      示例

                      abstract class A
                      {
                          protected A() {Console.WriteLine("Abstract class constructor"); }
                      }
                      //Derived class
                      class B : A
                      {
                         public B() {Console.WriteLine("Derived class constructor"); }
                      }
                      
                      class Program
                      {
                          static void Main(string[] args)
                          {
                              B obj = new B();
                          }
                      }
                      

                      输出将是

                      Abstract class constructor
                      Derived class constructor
                      

                      【讨论】:

                        猜你喜欢
                        • 2023-03-04
                        • 2018-06-10
                        • 2015-11-17
                        • 2013-11-17
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-04-01
                        • 2015-03-20
                        • 2011-03-21
                        相关资源
                        最近更新 更多