【问题标题】:c#: inheritance and abstract classes - base. vs :base (in constructor initializers)c#:继承和抽象类 - 基类。 vs :base (在构造函数初始化器中)
【发布时间】:2017-05-08 19:15:37
【问题描述】:

当我尝试如下设置构造函数时,出现错误:“Book 类型不包含采用 0' 参数的构造函数”:

using System;
using System.Collections.Generic;
using System.IO;
abstract class Book
{

    protected String title;
    protected  String author;

    public Book(String t,String a){
        title=t;
        author=a;
    }
    public abstract void display();

}

class MyBook : Book
{
    private int price = 0;

    public MyBook(string t, string a, int p)
    {
            base.title = t;
            base.author = a;
            this.price = p;
    }

    public override void display()
    {
        Console.WriteLine($"Title: {title}");
        Console.WriteLine($"Author: {author}");
        Console.WriteLine($"Price: {price}");
    }
}

我已将问题范围缩小到我如何使用 base 关键字。我应该在其构造函数声明中使用带有 base 关键字的基本构造函数初始化程序。

    public MyBook(string t, string a, int p)
    : base(t, a) 
    {
            this.price = p;
    }

请有人向我解释为什么我不能使用 base.在我的构造函数中?是因为 title 和 author 变量都是抽象类的受保护成员吗?我认为由于我是从 Book 派生的,我可以访问这些受保护的成员。还是因为我没有实例化我的子类,因此我不能这样设置我的构造函数?

【问题讨论】:

  • 因为您已经定义了一个基本构造函数,它需要 2 个参数才能初始化并为您服务它的成员。

标签: c# inheritance constructor abstract


【解决方案1】:

您可以按照您尝试的方式使用受保护的属性。这不是你遇到的问题。当您尝试创建派生类的实例时,程序需要知道调用基类的哪个构造函数来创建对象的该部分。如果未指定,则调用默认(无参数)构造函数。基类没有其中之一,因此您会收到错误消息。 :base(t,a) 有必要告诉它使用哪个构造函数,以及传递它的参数。如果你愿意,你可以这样做:

public MyBook(string t, string a, int p)
: base(t, a) 
{
        base.title = t;
        base.author = a;
        this.price = p;
}

当然,在这个例子中这很愚蠢,但是你可以看到它会让你以你尝试的方式使用这些属性。

【讨论】:

    【解决方案2】:

    如果您编写一个没有构造函数的类,编译器会为您定义一个无参数的默认构造函数,以方便编写简单的快速类。

    当您开始显式定义构造函数时,编译器假定您正在定义您想要的构造函数集。这使您可以控制如何创建类,同时保留编写使用默认构造函数的简单类的便利。

    MyBookBook。在MyBook 可以初始化自己之前,Book 必须首先这样做。但是这里没有要调用的无参数构造函数。 Book 已明确选择,就编译器而言,禁止您在此处执行的操作。该语言假定,如果您希望人们——子类、你的阿姨 Sally、任何人——能够使用无参数构造函数创建Book,你就会提供一个。

    public MyBook(string t, string a, int p)
    {
            base.title = t;
            base.author = a;
            this.price = p;
    }
    

    【讨论】:

      【解决方案3】:

      虽然有多种方法可以解决您的问题。我会尝试以更好的设计角度来回答,而不是仅仅解决您的问题。

      如果我要定义这样的类结构,我会这样做

      using System;
      using System.Collections.Generic;
      using System.IO;
      
      abstract class Book
      {
          protected String title;
          protected String author;
      
          public Book(String t, String a)
          {
              this.title = t;
              this.author = a;
          }
      
          public abstract void display();
      }
      
      class MyBook : Book
      {
          private int price = 0;
      
          public MyBook(string t, string a, int p) : base(t, a) // Let the base class decide what it wants to do with these arguments
          {
              this.price = p;
          }
      
          public override void display()
          {
              Console.WriteLine($"Title: {title}");
              Console.WriteLine($"Author: {author}");
              Console.WriteLine($"Price: {price}");
          }
      }
      

      【讨论】:

        【解决方案4】:

        你不能只设置基类的属性而不调用构造函数。因为基类有一个带参数的构造函数(并且没有默认构造函数),所以您必须显式调用基类的构造函数。

        public MyBook(string t, string a, int p)
            :base(t, a)
        {
            this.price = p;
        }
        

        创建派生类的实例时,实际上是在创建基类的实例。如果一个类具有需要参数的构造函数并且没有具有默认构造函数,那么创建它的唯一方法是调用这些构造函数之一。

        如果BookMyBook 在同一个程序集中,那么您可以添加一个只有继承类才能调用的protected 默认构造函数,如下所示:

        protected Book() { }
        

        但最好不要这样做。你把那个构造函数放在那里是有原因的,所以你不想要任何东西“绕过”那个构造函数——甚至是一个继承的类。

        例如,您可能决定 Book 不能有空 titleauthor,因此您在构造函数中放置验证以确保它们不为空。 MyBook 调用基类构造函数是件好事,因为现在总是会进行验证。

        【讨论】:

        • 其实他可以.. ctor 参数只是他错过的语言约束。
        • 他可以设置基类的属性。他不能只是设置基类的属性。 (是的,我可以说得更清楚。)
        • 好吧,你的刚刚让你解释了。 :)
        猜你喜欢
        • 2013-12-30
        • 1970-01-01
        • 2018-11-29
        • 1970-01-01
        • 2020-02-10
        • 2015-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多