news 2026/10/27 1:00:45

C# 构造函数详解:默认构造函数、空参构造函数与继承构造

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# 构造函数详解:默认构造函数、空参构造函数与继承构造

构造函数主要用来初始化对象,它分为静态(static)构造函数和实例(instance)构造函数两种类别。大家应该都了解如何编写类的构造函数,这里只说明默认构造函数的作用,以及在类中保留默认构造函数的重要性。更准确地说,是保留空参数构造函数的重要性。

一、从一个简单的类开始

我们先来写一个类 A,代码如下:

public class A { public int Number; // 数字 public string Word; // 文本 }

然后在 Test 类中实例化它:

public class Test { static void Main() { A a = new A(); // 实例化,A() 即为类 A 的默认构造函数 Console.WriteLine("Number = {0}\nWord = {1}", a.Number, a.Word); Console.Read(); } }

输出的结果是:

Number = 0 Word =

二、默认构造函数到底做了什么

在上面的示例中,我们并没有在 A 类中显式写出构造函数。当我们没有提供任何构造函数时,C# 会自动为类提供一个默认构造函数,其格式为:

public A() { // 空白的方法体 }

这个默认构造函数看起来似乎根本没什么作用:没有接收任何输入参数,方法体也是空的。那么它真的是无所事事、可有可无的吗?回想一下构造函数的主要作用——初始化对象。再看输出结果:int类型的Number被初始化为默认值0,string类型的Word被初始化为默认值(空引用,输出为空白字符)。这些工作,正是由这个看似没起作用的默认构造函数完成的。

三、自己提供构造函数后,默认构造函数去哪了

很快,A 类的设计让我们并不满意,我们加强了它的能力,自己提供了构造函数:

public class A { public int Number; public string Word; public A(int number, string word) { Number = number; // 标记 1 Word = word; // 标记 2 } }

Test 类也重新设计:

public class Test { static void Main() { A a = new A(100, "一百"); // 调用了新的带参数构造函数 Console.WriteLine("Number = {0}\nWord = {1}", a.Number, a.Word); Console.Read(); } }

输出结果:

Number = 100 Word = 一百

运行很顺利,结果正确。当我们提供了构造函数时,C# 认为我们已经意识到、并且有能力做好类的初始化工作,于是它撤销了默认构造函数,把控制权交到了我们手里。

那么我们自己的构造函数能胜任默认构造函数的工作吗?带着怀疑,我们做个尝试:将 A 类构造函数的方法体清空,即注释掉标记 1 和标记 2 两行,重新运行,输出结果是:

Number = 0 Word =

这个结果和调用默认构造函数的结果是一样的,说明我们自己提供的构造函数同样能担当默认构造函数的工作。

四、继承场景下的构造函数调用顺序

不久以后,业务进一步扩展,我们要新建一个类,它继承自类 A,我们把它称为类 B:

public class A { public int Number; public string Word; public A(int number, string word) { Number = number; Word = word; } } public class B : A { public string Name; // 增加一个成员 public B(int number, string word) { Number = number; Word = word; } }

修改 Test 类:

public class Test { static void Main() { B b = new B(100, "一百"); Console.WriteLine("Number = {0}\nWord = {1}", b.Number, b.Word); Console.Read(); } }

运行后出错,错误信息是:“A”方法没有采用“0”个参数的重载。为什么会这样呢?看起来我们并没有什么错误:A 类提供了构造函数,B 类也提供了构造函数。

事实上,B 类要完成构造,必须先完成 A 类的构造。当我们采用public B(int number, string word)来构造 B 类时,由于没有明确指明继承的构造方法,C# 默认我们继承的是基类的默认构造函数,等价于:

public B(int number, string word) : base()

采用这条构造函数时,C# 先调用类 A 的默认构造函数base()完成基类的构造,然后调用 B 类的构造函数完成针对 B 类构造的剩余工作。尝试修改上面的相应代码段为:

// 在类 A 中添加一个空参构造函数 public class A { // …… public A() { Number = 1; Word = "空参构造"; } // ……. } // 将类 B 构造函数改为继承 A 类空参构造 public B(int number, string word) : base()

实际上,我是多此一举了:public B(……) : base()与public B(……)是一样的,未显式指明构造函数的继承体时,C# 就默认为base()。

输出结果:

Number = 100 Word = 一百

注意看我们给 A 类添加的空参构造函数:我们在方法体内给Number赋值1,给Word赋值"空参构造"。然而从输出结果看,这些赋值都被覆盖了。这就说明,base()在前执行,而后才是public B(…..)的方法体。

五、错误原因与两种解决方案

回头来看我们那个错误的例子。遗憾的是,我们在类 A 中自己提供了构造函数,这导致 A 类的默认构造函数消失了;当 B 类从 A 类继承默认构造函数时,找不到入口,于是导致了如上的错误。

如果要避免错误,有两种方法:

  1. 在 A 类中添加一个无参数的构造函数;
  2. B 类构造函数显式继承 A 类的构造函数,例如: base(number, word)。

六、为什么 C# 不能自动识别并继承

事情已经解释得很清楚了,不过也许还有个小问题:在 B 类中,我们提供了一个和 A 类构造函数结构相同的构造函数,为什么 C# 不能自动识别、自动继承呢?

道理很简单:C# 不能靠猜测来判断我们提供的参数究竟是要用作初始化基类,还是用于初始化继承类。还是来看一个例子:

public class A { public int Number; public string Word; public A(int number, string word) { Number = number; Word = word; } } public class B : A { public string Name; public B(int number, string word, string name) : base(number, word) { Number = number; Word = word; Name = name; } } public class Test { static void Main() { B b = new B(100, "一百", "初始化B的参数"); Console.WriteLine("Number = {0}\nWord = {1}\nName = {2}", b.Number, b.Word, b.Name); Console.Read(); } }

输出结果是:

Number = 100 Word = 一百 Name = 初始化B的参数

注意在 B 类的构造函数中,我们提供了三个参数:其中前两个用于初始化 A 类中的成员,第三个用于初始化 B 类的成员。虽然父类构造函数与子类构造函数的参数数量不一样,但由于我们明确指定了继承关系,它们很好地完成了工作。

七、小结

  • 未显式定义构造函数时,C# 会自动提供一个无参的默认构造函数,负责把字段初始化为类型默认值。
  • 一旦显式定义了任何构造函数,编译器就不再自动生成默认构造函数。
  • 子类构造时会先调用基类构造函数,未显式指定时默认调用base(),因此基类必须存在可访问的无参构造函数。
  • 若基类只提供了带参构造函数,子类需通过: base(参数)显式指定要调用的基类构造函数。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 1:00:43

C# 类的继承:语法、实例与调用机制详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 0:53:44

agent-skills协议:可插拔AI技能的声明式交付标准

1. “agent-skills”不是功能模块,而是一套可插拔的能力交付协议你第一次在 GitHub 仓库名里看到agent-skills,或在 CLI 工具的--skills参数后跟一串路径时,大概率会下意识把它理解成“AI Agent 能做的几件事列表”——比如“查天气”“读文件…

作者头像 李华
网站建设 2026/10/27 0:43:42

Agent-Reach:轻量级多智能体触达与协作框架解析

1. 项目概述:Agent-Reach 到底是什么先把结论放前面:Agent-Reach 不是一个新的 AI 模型,也不是某个大厂的闭源平台,而是一个面向多智能体(Multi-Agent)协作场景的触达与协同框架。它的核心目标非常朴素——…

作者头像 李华
网站建设 2026/10/27 0:36:20

Labelme柑橘数据集转YOLO格式与YOLOv8训练避坑指南

简介:这套柑橘果目标检测数据集专为农业视觉与智能农机研究而构建,包含580张真实果园场景图片及对应Labelme标注文件,可支撑树上挂果与树下落果的区分识别,适用于果园自动化收获、成熟度监测、产量预估以及农业机器人视觉导航等任…

作者头像 李华
网站建设 2026/10/27 0:31:06

多语言语音模型的语言判别嵌入(LDE)架构设计

1. 项目概述:让语音模型真正“听懂”语言边界,而不是靠猜“语言判别增强多语言语音模型的语言学习能力”——这个标题乍看像论文摘要,但拆开来看,它直指当前多语言语音识别(ASR)和语音合成(TTS&…

作者头像 李华
网站建设 2026/10/27 0:21:22

影刀RPA进阶教程:商品标题优化助手——竞品标题词频分析自动化

影刀RPA进阶教程:商品标题优化助手——竞品标题词频分析自动化 改标题全靠感觉,是大部分中小商家标题优化的现状。竞品前二十名的标题里"免烫"“冰丝”"阔腿"这些词到底出现了多少次,哪个词是流量密码哪个词纯属凑字数&a…

作者头像 李华