c#中的构造函数

我在学习 c# 的构造函数,你看我的表述是否正确,首先我们创建一个类,这个类里面会有变量,在这个类函数外面我们可以调用,给这个类函数赋值,这个时候我们对其中一个变量的值去修改也是可以成功的,这个时候我们就要对这两个变量进行变量的构造,这个时候当我们构造数据了以后,就不能直接通过直接对变量赋值去修改这个数据了

你的表述梳理 & 纠错

先把你原话翻译成人话:

创建一个类,类里有变量;在类外面可以调用这个类、给类的变量赋值,也能修改变量值。用构造函数之后,就不能在外部直接赋值修改变量了。

结论:后半句不完全正确,有概念混淆,我帮你拆开讲清楚。

1、没有构造函数的时候

C# 如果你不写任何构造函数 ,编译器会自动给你一个无参默认构造函数

复制代码
class Person
{
    // 公开字段
    public string name;
    public int age;
}

// 外部使用
Person p = new Person(); // 调用编译器默认给的无参构造
p.name = "张三"; // ✅ 外部直接赋值修改
p.age = 20;
p.age = 25;     // ✅ 外部再次修改,完全可以

关键点:之所以外面能直接改,是因为字段标记了 public不是因为有没有构造函数

2、写了构造函数会发生什么

构造函数作用:创建对象的时候,给成员做初始化赋值 ,它本身不会禁止外部修改变量

复制代码
class Person
{
    public string name;
    public int age;

    // 手写有参构造函数
    public Person(string n, int a)
    {
        name = n;
        age = a;
    }
}

// 创建对象时必须走构造函数初始化
Person p = new Person("张三",20);

p.age = 99; // ✅!依然可以外部直接修改!

👉 重点:就算写了构造函数,只要字段是 public,外面照样改。构造函数只负责「对象出生那一刻初始化」,不锁死变量。

3、什么时候外部不能直接修改?

不是构造函数干的,是访问修饰符 + 属性(get/set)

  • 把字段改成 private,外部就不能直接访问;

  • 或者属性只写 get,去掉 set,变成只读。

    class Person
    {
    // private 私有字段,类外面看不见
    private string name;
    private int age;

    复制代码
      public Person(string n, int a)
      {
          name = n;
          age = a;
      }
    
      // 只读属性:只能读,外部不能赋值
      public string Name => name;
      public int Age => age;

    }

    Person p = new Person("张三",20);
    Console.WriteLine(p.Age);
    // p.Age = 100; // ❌编译报错,不能修改

4、修正你的完整正确表述

我们创建一个类,类中定义成员变量。如果成员是公开的,在类外部实例化对象之后,可以读取、直接给这些变量赋值、修改值。

构造函数的作用:在 new 创建对象的瞬间,给成员做初始化。构造函数本身不会阻止外部修改变量。

如果想要外部不能直接修改数据:需要把成员私有化,配合只读属性限制,而不是靠构造函数。构造只是初始化,不等于锁死数据。

容易踩坑的误区总结

  1. ❌错误:写了构造函数,外部就不能改字段 ✅正确:构造函数 = 对象初始化工具;能不能改看访问权限(public/private)和 set 访问器。
  2. ❌错误:不写构造函数,类就没有构造函数 ✅正确:不手写,C# 编译器自动生成无参构造;一旦手写任意构造,编译器不再提供默认无参构造。

如果你愿意,我可以写两段极简对比代码,一段可修改,一段初始化后只读,方便你跑一遍加深理解。


那我初始化和不初始化有什么区别呢

C#:初始化 vs 不初始化

先分清两个概念:

  1. 初始化 :对象刚诞生(new 的那一刻)就给变量一个确定的值
  2. 不初始化:创建完对象,字段用默认值,之后你再手动赋值

构造函数就是做初始化最常用的手段。

1、不初始化(靠默认值,之后手动赋值)

C# 所有字段都有默认值

  • 数值类型 int0

  • stringnull

  • 引用类型 → null

    class Student
    {
    public string name;
    public int age;
    }

    // new 出来,调用默认无参构造,没有做初始化
    Student s = new Student();

    Console.WriteLine(s.name); // null
    Console.WriteLine(s.age); // 0

    // 创建完对象以后,再手动赋值
    s.name = "小明";
    s.age = 18;

特点:

  • 对象刚创建出来,字段是系统给的默认值,可能是 null0
  • 允许创建出一个 "半成品对象",你后面再补数据;
  • 风险:忘记手动赋值,就拿着 null / 0 去用,容易出 bug。

2、用构造函数初始化(new 的同时就把数据给好)

复制代码
class Student
{
    public string name;
    public int age;

    // 构造函数:new 的瞬间完成初始化
    public Student(string n, int a)
    {
        name = n;
        age = a;
    }
}

// new 的时候必须传入参数,对象一诞生就有有效数据
Student s = new Student("小明", 18);

Console.WriteLine(s.name); // 小明
Console.WriteLine(s.age);  // 18

特点:

  • ✅对象一被创建出来,就具备有效业务数据,不会出现 null0 这种无效半成品;

  • ⚠️你不能写出 Student s = new Student();,手写了有参构造,编译器不再赠送无参构造;

  • ⚠️仅仅只是初始化! 只要字段是 public,创建完依旧可以改:

    s.age = 99; // 依旧可以修改,初始化只是出生那一刻赋值,不是永久锁死


核心对比表格

表格

不初始化(默认构造,后续手动赋值) 构造函数初始化
对象刚 new 出来的值 系统默认值:0、null 你传入的业务有效值
创建对象 new Student() new Student("小明",18)
是否能外部修改 public 字段可以改 public 字段照样可以改
风险 容易忘记赋值,拿到 null,报错 强制创建时提供数据,避免半成品
作用时机 new 之后,由程序员手动赋值 new 对象这一瞬间完成赋值

通俗大白话比喻

  • 不初始化:生个孩子,先让他出生,名字、年龄以后慢慢填。有可能忘记填,孩子没名字。
  • 构造函数初始化 :生孩子的时候,出生那一刻就把名字年龄全部定好。但是出生之后依旧可以改名改年龄(public 字段)

关键点再次强调: 初始化 ≠ 只读、不可修改。初始化只管控 "出生那一刻";能不能后续修改,看访问修饰符和 set。

拓展:字段直接初始化(不写构造函数也能初始化)

除了构造函数,还可以直接给字段写初始值,这也是初始化:

复制代码
class Student
{
    public string name = "未知"; // 字段初始化
    public int age = 0;
}

new 对象的时候,还没跑构造函数,就先把这两个值填进去。

什么时候选哪种?

  1. 有些对象必须要有数据才能正常工作,不能允许半成品 → 用有参构造强制初始化。
  2. 有些对象可以先创建,后面慢慢填充数据 → 使用无参构造,后续手动赋值。

如果你想,我可以写一段小例子演示:不初始化拿到 null 引发报错,对比初始化之后规避这个问题。


就是加了构造函数,到创建实例,就必须对这个实例赋值的意思吗

不完全是,只有手写了【有参构造函数】才会强制你 new 的时候传参数;如果你写的是无参构造,依然不用赋值

情况 1:写了有参构造函数

复制代码
public class Person
{
    public string Name;
    public int Age;

    // 有参构造
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

这时:

复制代码
Person p = new Person(); // ❌报错!不能这样写
Person p = new Person("李四",22); // ✅必须传入参数,创建实例的时候就要给值

这就是你理解的:创建实例时必须赋值。 原因:一旦手写任意构造函数,C# 就不再自动生成默认无参构造。

⚠️但是!只是 new 这一步必须给初始值,实例创建完成之后,p.Name = "王五" 照样可以修改,构造函数只管出生瞬间。


情况 2:写的是无参构造函数

复制代码
public class Person
{
    public string Name;
    public int Age;

    // 无参构造,没有参数
    public Person()
    {
        Name = "默认名字";
        Age = 10;
    }
}

Person p = new Person(); // ✅不用传参数,直接创建实例

这里也有构造函数,但创建实例不需要外部传值,构造函数内部自己给了初始值。


情况 3:完全不写任何构造函数

编译器偷偷给你生成一个看不见的无参默认构造函数

复制代码
public class Person
{
    public string Name;
    public int Age;
}

Person p = new Person(); // ✅可以直接new,字段拿到默认null、0

总结一句话

  1. 只有【有参构造函数】,才强制你在new创建实例的时候传入数值;
  2. ✅无参构造(手写 / 编译器默认),new 的时候不需要传值;
  3. ❗强制的只是创建时的初始化 ,不等于对象以后不能改;能不能后续修改看字段是不是public

补充小细节

如果你既想要:new 的时候可以不传参,又支持 new 的时候传参,可以重载构造函数,写两个构造:

复制代码
public class Person
{
    public string Name;
    public int Age;

    //无参
    public Person()
    {
        
    }
    //有参
    public Person(string name,int age)
    {
        Name = name;
        Age = age;
    }
}
//两种都能用
Person p1 = new Person();
Person p2 = new Person("张三",18);

那无参构造和完全不写,其实是一样的啊

表面看效果很像,但底层不是一回事,我把区别讲透。

1、完全不写任何构造函数

复制代码
class Person
{
    public string Name;
    public int Age;
}

编译器自动帮你生成一个隐藏的无参构造函数。 等价于编译器偷偷插入这段代码(你源码看不见):

复制代码
public Person()
{
    // 里面什么代码都没有
}

使用:

复制代码
Person p = new Person();

对象字段得到系统默认值:Name=nullAge=0

2、自己手写一个无参构造函数

复制代码
class Person
{
    public string Name;
    public int Age;

    // 自己写的无参构造
    public Person()
    {
        Name = "无名氏"; // 这里可以写自己的初始化逻辑!
        Age = 1;
    }
}

Person p = new Person();

对象出来:Name="无名氏"Age=1


核心区别

① 编译器自动生成的无参构造(不写构造函数)

  • 函数体里面是空的,什么都不做
  • 只会让字段拿到 C# 系统默认值(null、0、false);
  • 只要你手写任意一个构造(不管有参无参),这个自动生成的就直接消失!

② 手写无参构造

  • 你可以在大括号 { } 里面写代码,做自定义初始化; 比如给字段设置默认业务值、打印日志、做计算;
  • 它是实实在在写在类里的构造,不会自动消失。

👉 只有当手写无参构造里面完全空着什么代码都不写的时候,运行效果才和 "完全不写构造函数" 看起来几乎一样。

复制代码
//手写空的无参构造
public Person()
{
    //啥也不干
}

此时运行效果 ≈ 不写构造函数。但身份上:一个是你手写,一个是编译器偷偷生成。

一个非常关键的坑(考试、写代码高频踩坑)

看这个例子:写了一个有参构造,不再写无参

复制代码
class Person
{
    public string Name;
    public int Age;

    //只写有参构造
    public Person(string n,int a)
    {
        Name = n;
        Age = a;
    }
}

Person p = new Person(); 直接编译报错!

原因:只要你手写了任意构造函数(有参),编译器就不再给你生成那个默认无参构造。 哪怕你想要无参,必须自己手动敲出来。

如果你完全不写任何构造函数,编译器才会送你无参版本。

简短总结

  1. 不写构造函数:编译器赠送空无参构造,内部啥逻辑都没有。
  2. 手写空无参构造:代码是你写的,啥逻辑也没有,运行表现同上。
  3. 手写无参构造里面写代码:new 的时候就执行你写的初始化逻辑,这就和 "不写构造函数" 不一样。
  4. 只要手写任意构造函数,编译器赠送的默认无参构造就没了。

记忆小口诀

不写构造,编译器送你空无参; 只要手写任意构造,赠送福利直接取消。

要不要我给你出 2 行小思考题,检验下你是不是吃透这个点?

今天 19:28

就是无参里面可有个值,而不写就是里面有个默认值

对,你理解方向对,我帮你把这句话精准修正下:

手写无参构造函数:你可以在函数体内自己指定初始值 (想设什么就设什么)。 完全不写构造函数:编译器生成空构造,不会帮你设置业务值,只给 C# 系统默认值(int=0,string=null)。

例子 1:手写无参,自己赋值

复制代码
public Person()
{
    Name = "匿名";  // ✅我自己规定的初始值
    Age = 18;
}

new 出来直接:Name="匿名",Age=18。

例子 2:完全不写构造函数

编译器生成空构造,函数里面一行代码都没有。

复制代码
//编译器偷偷生成,内部空
public Person(){}

new 出来:Name=null,Age=0。这是系统类型默认值,不是你写的。


容易混淆的点分清

  1. 手写无参构造可以是空的 public Person(){},里面什么都不写。 此时效果就和 "不写构造函数"表现一模一样,出来也是 null、0。

区别只是:一个是你手写,一个编译器生成;一旦你写了有参构造,编译器版本会消失,手写的不会消失。

  1. "不写构造函数" 本身不会主动设置任何业务值,仅仅是 C# 语法规则:所有类字段一定会被赋予系统默认值。

大白话总结

  • ✍️手写无参构造:出生的时候,由你来决定给什么初始值
  • 🤖不写构造函数:出生的时候,编译器什么都不干,C# 系统自动给类型默认值(0/null)

补充:字段直接写 public string Name="匿名"; 这种,属于字段初始化,比构造函数更早执行,又是另外一套机制。

相关推荐
可乐鸡翅yeah_1 小时前
hls.js 多实例并发踩坑,多视频页面播放器内存与冲突问题解决
开发语言·前端·javascript·音视频·hls·m3u8·m3u8在线播放
zbyyd1 小时前
Linux 系统编程 :select/epoll 多路复用与 SQLite 数据库实战
linux·数据库·sqlite
养生技术人1 小时前
Oracle OCP认证考试题目详解082系列第17题
运维·数据库·sql·oracle·ocp
hahaha60161 小时前
HLS高层次综合设计--axi之burst纠偏
开发语言·算法·fpga开发·c#
SunnyDays10111 小时前
如何使用 Python 给 PDF 添加附件:文档附件与附件注释
开发语言·python·pdf
koi77u1 小时前
嵌入式学习———sqlite数据表
linux·数据库·学习·sqlite
xx~t1 小时前
嵌入式——Linux软件编程——数据库
linux·c语言·数据库·oracle·sqlite
ACP广源盛139246256731 小时前
M6/M5 Pro Mac mini 端侧 AI 爆发@ACP#YLB3118 存储扩展芯片在本地 AI 服务中的机会与落地场景
大数据·网络·数据库·人工智能·嵌入式硬件·macos
学逆向的1 小时前
扩展PE头属性说明
开发语言·网络安全·pe