🧩 C#面向对象编程(OOP)→ “智能玩具工厂”
1. 核心思想:用「对象」组装程序
想象你在开一家玩具工厂:
设计模具(类)→ 比如”变形金刚模具”
生产玩具(对象)→ 用模具造出100个变形金刚
玩具自带功能(方法)→ 能变形、能发光
C#代码示例:
// 1. 设计模具(类)
public class 变形金刚
{
// 玩具属性(特征)
public string 名字 { get; set; }
public string 颜色 { get; set; }
// 玩具功能(方法)
public void 变形()
{
Console.WriteLine($"{名字}开始变形!咔嚓咔嚓...");
}
}
// 2. 生产玩具(对象)
var 擎天柱 = new 变形金刚 { 名字 = "擎天柱", 颜色 = "红色" };
var 大黄蜂 = new 变形金刚 { 名字 = "大黄蜂", 颜色 = "黄色" };
// 3. 使用功能
擎天柱.变形(); // 输出:擎天柱开始变形!咔嚓咔嚓...
🔧 四大核心特性(工厂规则)
① 封装 → 玩具的「包装盒」
隐藏内部零件(私有字段)
只暴露按钮(公共属性和方法)
public class 电饭煲
{
private int _温度; // 隐藏内部零件
// 公共按钮
public void 开始煮饭()
{
_加热();
Console.WriteLine("饭好了🍚");
}
private void _加热() // 内部逻辑
{
_温度 = 100;
}
}
② 继承 → 「模具的亲子关系」
儿子模具自动拥有父亲模具的所有功能
public class 汽车
{
public void 启动() => Console.WriteLine("轰隆隆...");
}
public class 电动车 : 汽车 // 继承
{
public void 充电() => Console.WriteLine("滋滋滋...");
}
var 特斯拉 = new 电动车();
特斯拉.启动(); // 继承自汽车
特斯拉.充电(); // 自己的新功能
③ 多态 → 「万能遥控器」
同一个按钮,不同设备有不同反应
public class 设备
{
public virtual void 开启() => Console.WriteLine("设备启动");
}
public class 空调 : 设备
{
public override void 开启() => Console.WriteLine("空调制冷❄️");
}
设备 我的设备 = new 空调();
我的设备.开启(); // 输出:空调制冷❄️(实际调用子类方法)
④ 抽象 → 「概念设计图」
只规定必须有的功能,不实现细节
public abstract class 电器
{
public abstract void 开关(); // 抽象方法(无具体实现)
}
public class 风扇 : 电器
{
public override void 开关() => Console.WriteLine("风扇转动🌀");
}
🏭 C#特色OOP工具
工具 | 作用 | 示例 |
---|---|---|
属性 | 控制字段访问(get/set) | public int Age { get; private set; } |
接口 | 强制实现指定功能 | interface I飞行 { void 飞(); } |
结构体 | 轻量级对象(适合小数据) | public struct 坐标 { public int X, Y; } |
泛型 | 可适配多种类型的模具 | public class 盒子<T> { public T 内容; } |
接口示例:
public interface I攻击
{
void 攻击();
}
public class 激光枪 : I攻击
{
public void 攻击() => Console.WriteLine("biu biu biu~");
}
💡 为什么用C# OOP?
像工厂流水线一样组织代码 → 复杂系统变清晰
避免重复代码 → 相同功能通过继承复用
轻松维护 → 修改一个类不影响其他模块
🚀 终极总结
C# OOP就是把程序变成「智能玩具工厂」:
类是模具,对象是生产的玩具
封装/继承/多态/抽象是工厂的生产规则
属性/接口/泛型是特种工具
试试这个例子:
public class 猫咪
{
public string 名字 { get; set; }
public virtual void 叫() => Console.WriteLine("喵~");
}
public class 布偶猫 : 猫咪
{
public override void 叫() => Console.WriteLine("优雅的喵~");
}
猫咪 猫1 = new 猫咪 { 名字 = "橘座" };
猫咪 猫2 = new 布偶猫 { 名字 = "仙女" };
猫1.叫(); // 输出:喵~
猫2.叫(); // 输出:优雅的喵~
🌐 命名空间(Namespace)→ 编程的「行政区划」
1. 最简理解(快递地址系统)
想象你在一个超大城市送快递:
没有命名空间:所有地址都是人民路15号
(全城可能有100个同名地址)
有命名空间:北京市.朝阳区.人民路15号
vs 上海市.黄浦区.人民路15号
C#示例:
// 不同命名空间的同名类
namespace 北京分公司
{
class 员工 { public string 姓名 = "张三"; }
}
namespace 上海分公司
{
class 员工 { public string 姓名 = "李四"; }
}
// 使用时明确指明"行政区"
var 北京员工 = new 北京分公司.员工();
var 上海员工 = new 上海分公司.员工();
🧩 核心作用
场景 | 无命名空间 | 有命名空间 |
---|---|---|
类名冲突 | ❌ 编译报错 | ✅ 公司A.类 vs 公司B.类 |
代码组织 | 所有类堆在一起 | 按功能/模块分层归类 |
团队协作 | 需人工加前缀XX_类名 |
天然隔离(如部门A.报表工具 ) |
📦 常见命名空间(内置”行政区”)
using System; // 基础服务(如Console)
using System.IO; // 文件读写
using System.Threading; // 多线程
using UnityEngine; // Unity游戏开发
文件示例:
// 文件:北京市/朝阳区/人民路.cs
namespace 北京市.朝阳区
{
public class 人民路 { /*...*/ }
}
⚡ 使用技巧
默认隐藏机制
// 需要手动"开地图"
using 北京分公司;
var 员工 = new 员工(); // 自动找北京分公司下的类
嵌套命名空间
namespace 集团.研发部.CSharp组
{
class 代码生成器 { /*...*/ }
}
别名解决重名
using 北京员工 = 北京分公司.员工;
using 上海员工 = 上海分公司.员工;
🌰 现实开发场景
不同公司的SDK共存
using 阿里云.存储;
using 腾讯云.数据库;
Unity插件隔离
namespace MyGame.Plugins.AI
{
class 行为树 { /*...*/ }
}
内部模块划分
namespace 电商系统
{
namespace 订单模块 { /*...*/ }
namespace 支付模块 { /*...*/ }
}
💡 为什么重要?
避免类名污染
→ 就像不能有同名身份证号
提高可读性 → 看到System.IO.File
就知道是文件操作
模块化开发 → 不同团队代码通过命名空间天然隔离
🚀 终极口诀
命名空间是代码的户籍系统,让张三
可以存在于北京/上海/深圳
而不冲突!
试试这个例子:
namespace 学校A
{
class 学生 { public string 名字 = "小明"; }
}
namespace 学校B
{
class 学生 { public string 名字 = "小红"; }
}
// 使用
var 学生1 = new 学校A.学生();
var 学生2 = new 学校B.学生();
Console.WriteLine(学生1.名字); // 输出"小明"
📜 C#访问管理关键字大全(极简版)
控制代码的「可见度」就像调节房间的隐私级别
1. 成员访问控制
关键字 | 权限范围 | 现实比喻 |
---|---|---|
public |
完全公开(所有代码可访问) | 公园长椅(谁都能坐) |
private |
仅当前类内部可用 | 日记本(仅自己看) |
protected |
当前类及子类可用 | 家族相册(自己和子女能看) |
internal |
同一程序集(项目)内可用 | 公司内网(只有员工能访问) |
protected internal |
protected + internal 的并集 |
公司员工及其外包团队 |
private protected |
当前类及同一程序集中的子类 | 家族企业核心机密(限家族员工) |
示例:
class 家庭相册
{
public string 全家福 = "公开照片"; // 所有人可见
private string 私房钱位置 = "床底下"; // 仅本类可见
protected string 祖传秘方 = "红烧肉配方"; // 子女可见
}
2. 类/结构体访问控制
关键字 | 适用场景 | 示例 |
---|---|---|
public |
可被其他程序集引用 | public class 工具类 { } |
internal |
仅当前项目可用(默认) | internal struct 坐标 { } |
abstract |
抽象类(不能实例化) | public abstract class 动物 { } |
sealed |
密封类(禁止继承) | public sealed class 字符串工具 { } |
3. 特殊访问场景
关键字 | 作用 | 示例 |
---|---|---|
readonly |
字段只能在构造函数中修改 | public readonly int ID = 1; |
const |
编译时常量(完全不可变) | public const float PI = 3.14f; |
static |
属于类而非实例 | public static int 计数器 = 0; |
partial |
拆分类定义到多个文件 | public partial class 大文件 { } |
⚡ 黄金搭配组合
数据保护三件套:
private string _密码; // 字段私有化
public string 密码 { get; private set; } // 外部只读
安全继承方案:
public abstract class 基类 { } // 必须被继承
public sealed class 最终版 : 基类 { } // 不能再被继承
💡 访问控制的本质
通过关键字控制代码的「可触碰范围」,像给不同人发不同门禁卡:
public
→ 万能卡
private
→ 个人保险箱钥匙
protected
→ 家族通行证
一句话原则:
默认用private
,必要才放开!
🍎 类型 → 数据的「分类标签」
现实比喻:
就像水果店的分类框:
int
框放苹果(整数)
string
框放香蕉(文字)
bool
框放榴莲(只有true/false两种状态)
C#示例:
int 苹果数量 = 5; // 整数类型
string 香蕉品牌 = "都乐"; // 字符串类型
bool 榴莲已熟 = true; // 布尔类型
特点:
告诉编译器这个变量能存什么数据
就像盒子上的标签,防止你把牛奶倒进装乒乓球的箱子里
🏷️ 属性 → 对象的「特征描述」
现实比喻:
就像你的身份证:
姓名
(字符串类型)
年龄
(整数类型)
是否已婚
(布尔类型)
C#示例:
public class 人
{
// 自动属性(智能字段)
public string 姓名 { get; set; } = "张三";
// 完整属性(带逻辑控制)
private int _年龄;
public int 年龄
{
get => _年龄;
set => _年龄 = value > 0 ? value : 0; // 禁止负数年龄
}
}
var 我 = new 人();
我.年龄 = -5; // 实际会被修正为0
Console.WriteLine(我.姓名); // 输出:张三
属性 vs 字段:
字段 | 属性 | |
---|---|---|
作用 | 直接存储数据 | 控制数据的访问方式 |
示例 | public int age; |
public int Age { get; set; } |
优势 | 简单直接 | 可添加验证逻辑/计算逻辑 |
🛠️ 方法 → 对象的「行为能力」
现实比喻:
就像手机的功能按钮:
打电话()
→ 需要输入号码(参数)
拍照()
→ 返回一张照片(返回值)
关机()
→ 不需要额外信息(无参数)
C#示例:
public class 手机
{
// 无返回值方法
public void 关机()
{
Console.WriteLine("正在关机...");
}
// 带参数和返回值的方法
public string 打电话(string 号码)
{
return $"正在呼叫:{号码}";
}
}
var 我的手机 = new 手机();
我的手机.关机();
string 状态 = 我的手机.打电话("13800138000");
方法三要素:
输入(参数):string 号码
处理:拼接字符串
输出(返回值):string
类型的状态
🧩 三者的关系 → 乐高积木组合
public class 汽车 // 类型(积木模具)
{
public string 品牌 { get; set; } // 属性(积木特征)
public void 加速(int 油门力度) // 方法(积木功能)
{
Console.WriteLine($"{品牌}车加速{油门力度}档!");
}
}
var 我的车 = new 汽车 { 品牌 = "丰田" };
我的车.加速(3); // 输出:丰田车加速3档!
📚 对比总结表
概念 | 比喻 | 特点 | 示例 |
---|---|---|---|
类型 | 数据分类框 | 决定能存什么数据 | int , string , bool |
属性 | 对象特征卡 | 控制对特征的访问 | public int Age { get; set; } |
方法 | 对象功能按钮 | 通过输入得到输出 | public string 呼叫(string 号码) |
💡 为什么重要?
类型安全 → 防止把文字当数字计算
属性封装 → 保护数据有效性(如年龄不能为负)
方法复用 → 像手机”拍照”功能,可以反复调用
试试这个例子理解三者协作:
public class 灯泡
{
public bool 是否亮着 { get; private set; } // 属性
public void 开关() // 方法
{
是否亮着 = !是否亮着;
Console.WriteLine(是否亮着 ? "灯泡亮了💡" : "灯泡灭了🌑");
}
}
var 客厅灯 = new 灯泡();
客厅灯.开关(); // 输出:灯泡亮了💡
🧱 在C#中定义类(Class)→ 就像「设计一张产品说明书」
1. 最简版定义(空壳类)
public class 手机 // ← 类名
{
// 这里是属性和方法的存放区(目前是空的)
}
public
:表示这个”说明书”可以公开使用
class
:声明这是一个类(不是接口/结构体)
手机
:类名(建议用帕斯卡命名法,如iPhone14
)
2. 完整版定义(含属性+方法)
public class 电饭煲
{
// 属性 → 产品参数
public string 品牌 { get; set; } = "美的"; // 默认值
public int 容量 { get; set; } // 单位:升
// 方法 → 产品功能
public void 开始煮饭()
{
Console.WriteLine($"{品牌}电饭煲正在煮{容量}升饭...");
}
public bool 是否煮熟(int 分钟)
{
return 分钟 > 30; // 30分钟以上算煮熟
}
}
🛠️ 使用这个类(生产具体产品)
// 实例化对象(根据说明书造产品)
var 我家电饭煲 = new 电饭煲
{
容量 = 5 // 设置属性
};
// 调用方法
我家电饭煲.开始煮饭(); // 输出:美的电饭煲正在煮5升饭...
bool 熟了 = 我家电饭煲.是否煮熟(40);
Console.WriteLine(熟了 ? "饭熟了🍚" : "再等等⏳");
📝 定义类的关键要点
部分 | 作用 | 示例 |
---|---|---|
类名 | 类型的标识符 | class 学生 { } |
属性 | 对象特征(数据) | public int Age { get; set; } |
方法 | 对象行为(功能) | public void 跑() { } |
构造函数 | 初始化对象的特殊方法 | public 学生() { } |
⚡ 自动生成类的快捷键(VS实用技巧)
新建类文件:Ctrl + Shift + A
→ 选择”类”
快速生成属性:输入 prop
→ 按两次Tab
public int MyProperty { get; set; } // 自动生成
生成构造函数:输入 ctor
→ 按Tab
public 电饭煲() { } // 自动生成
🌰 生活化类比
编程概念 | 现实对应物 | 关系说明 |
---|---|---|
类 | 产品设计图纸 | 定义了能生产哪些产品 |
对象 | 根据图纸生产的实物 | 每个产品都是独立的 |
属性 | 产品参数(颜色/重量) | 描述产品特征 |
方法 | 产品功能(开机/加热) | 让产品执行操作 |
💡 为什么用类?
代码复用:一张图纸能造无数个同类产品
逻辑封装:电饭煲内部电路对用户不可见(只管按按钮)
易于维护:修改图纸即可影响所有产品
试试这个完整示例:
public class 空调
{
public string 型号 { get; set; } = "格力KFR-35GW";
private int _温度 = 26; // 私有字段
// 属性(带温度校验)
public int 温度
{
get => _温度;
set => _温度 = (value >= 16 && value <= 30) ? value : _温度;
}
public void 制冷()
{
Console.WriteLine($"{型号}正在{温度}℃制冷❄️");
}
}
// 使用类
var 客厅空调 = new 空调 { 温度 = 22 };
客厅空调.制冷(); // 输出:格力KFR-35GW正在22℃制冷❄️
🏭 实例化 → 把「设计图」变成「实物」
1. 最简实例化(造基础款)
// 设计图
public class 手机
{
public string 品牌 { get; set; }
}
// 生产实物
手机 我的手机 = new 手机(); // ← 关键new操作
我的手机.品牌 = "华为";
Console.WriteLine(我的手机.品牌); // 输出:华为
new
:相当于工厂的「生产按钮」
手机()
:调用默认构造函数(隐藏的无参数构造)
🎨 进阶实例化(带个性化定制)
方式1:先造后改(流水线模式)
var 员工 = new 员工信息();
员工.姓名 = "张三";
员工.工号 = 10086;
方式2:造时即改(个性化定制)
var 员工 = new 员工信息
{
姓名 = "李四", // ← 对象初始化器
工号 = 10010
};
方式3:强制要求(构造函数设门槛)
public class 订单
{
public 订单(string 订单号) // 带参数的构造
{
this.订单号 = 订单号;
}
public string 订单号 { get; }
}
// 必须提供订单号才能创建
var 订单 = new 订单("DD20230729");
⚡ 内存角度理解(超重要!)
手机 a = new 手机(); // 在堆内存开房间
手机 b = a; // b拿到a的房间钥匙(指向同一对象)
a.品牌 = "苹果";
Console.WriteLine(b.品牌); // 输出:苹果(因为改的是同一个房间)
操作 | 内存表现 | 类比 |
---|---|---|
new 手机() |
在堆内存分配新空间 | 开发商新建一栋房 |
b = a |
复制钥匙(引用传递) | 给你同一栋房的钥匙 |
🛠️ 实例化的5个关键细节
必须用new
(值类型除外)
// 错误示范
手机 坏例子; // 只声明未实例化
坏例子.品牌 = "小米"; // ❌ 运行时报错!
构造函数是「出厂设置」
public class 学生
{
public 学生() // 构造函数
{
Console.WriteLine("新生入学");
}
}
可以批量生产
手机[] 仓库 = new 手机[100]; // 准备100个位置
仓库[0] = new 手机(); // 第一个位置放手机
var
让代码更简洁
var 实例 = new 复杂类名(); // 自动推断类型
实例化时内存自动回收
{
var 临时 = new 对象(); // 进入作用域时分配内存
} // 离开作用域后,内存会被GC自动回收
🌰 现实场景类比
编程操作 | 现实对应 |
---|---|
new 手机() |
工厂组装一台新手机 |
手机 备用机 = 主用机 |
把手机借给朋友用(同一台设备) |
手机 新机 = new 手机() |
买一台全新手机 |
💡 为什么需要实例化?
图纸≠实物:设计图不能直接打电话,必须造出手机
独立存储:每个对象有独立的内存空间(如不同玩家的游戏角色)
动态创建:运行时按需生产对象(如电商下单才生成订单)
🚀 终极口诀
new
一下,内存划,构造函数做初始化,从此对象可调用!
试试这个例子:
public class 灯泡
{
public bool 是否亮着 { get; set; }
public void 切换状态() => 是否亮着 = !是否亮着;
}
var 客厅灯 = new 灯泡();
客厅灯.切换状态();
Console.WriteLine(客厅灯.是否亮着 ? "灯亮了💡" : "灯灭了🌑");
🗝️ C#关键字 → 编程语言的「交通信号灯」
1. 基础关键字(红绿灯级别)
关键字 | 作用 | 通俗比喻 |
---|---|---|
if |
条件判断 | 岔路口指示牌:”如果下雨→走左边” |
for |
循环执行 | 洗衣机定时器:”转5圈后停止” |
using |
自动释放资源 | 快递柜取件:用完自动关门 |
try-catch |
错误处理 | 防摔手机壳:摔了也不碎屏 |
示例:
if (下雨了)
{
Console.WriteLine("带伞☔");
}
else
{
Console.WriteLine("戴墨镜😎");
}
🏗️ 类型相关(建筑工具)
关键字 | 作用 | 现实类比 |
---|---|---|
class |
定义类 | 建筑设计图 |
struct |
定义结构体 | 简易帐篷图纸(比房子轻量) |
enum |
枚举类型 | 菜单编号(1.咖啡 2.茶) |
interface |
定义接口 | USB标准:只要符合就能插 |
示例:
enum 饮料 { 可乐, 雪碧, 橙汁 }
饮料 我的选择 = 饮料.橙汁;
🔐 访问控制(权限管理)
关键字 | 作用 | 类比 |
---|---|---|
public |
完全公开 | 公园长椅(谁都能坐) |
private |
仅内部可用 | 日记本(仅自己看) |
protected |
家族传承 | 祖传秘方(只传子女) |
internal |
公司内部使用 | 员工食堂(外人不能进) |
示例:
class 银行账户
{
private decimal _余额; // 私密金库
public void 存钱(decimal 金额) { _余额 += 金额; } // 公开操作口
}
⚡ 高级特性(黑科技)
关键字 | 作用 | 类比 |
---|---|---|
async |
异步执行 | 外卖订单:下单后不用等送达 |
await |
等待异步完成 | 等快递时刷手机 |
delegate |
委托(函数指针) | 代金券:凭券兑换服务 |
event |
事件机制 | 门铃:按铃触发通知 |
示例:
async Task 煮面()
{
await 烧水(); // 等水开时不用傻等
Console.WriteLine("下面🍜");
}
📌 特殊标记(便利贴)
关键字 | 作用 | 类比 |
---|---|---|
var |
自动推断类型 | 盲盒:拆开才知道是啥 |
const |
定义常量 | 刻石碑:内容永不变 |
readonly |
运行时常量 | 纹身:纹完后不能改 |
nameof |
获取变量名 | 点名册:防止写错名字 |
示例:
const int 最大尝试次数 = 3;
Console.WriteLine($"请检查{nameof(最大尝试次数)}"); // 输出"最大尝试次数"
💡 为什么记关键字?
避免冲突:这些词不能用作变量名(像不能给孩子取名”法律”)
提高效率:VS智能提示依赖关键字识别
理解框架:ASP.NET等框架大量使用async
/await
等特性
🚀 速记口诀
if
判断for
循环,class
造对象,public
开门private
藏,async
让你不卡顿!
试试这个综合例子:
public class 智能家居
{
private bool _灯开关 = false;
public void 切换灯光()
{
_灯开关 = !_灯开关;
Console.WriteLine(_灯开关 ? "灯亮了💡" : "灯灭了🌑");
}
}
var 我家 = new 智能家居();
for (int i = 0; i < 3; i++)
{
我家.切换灯光(); // 灯会闪三次
}
🎁 数据类型 → 数据的「分类收纳盒」
1. 最简理解(超市储物柜版)
想象超市的储物柜有三种格子:
整数型
柜子 → 只能放1、2、3
这类完整数字
小数型
柜子 → 专放3.14、0.5
这类带小数点的
文字型
柜子 → 只收"你好"、
“ABC”`这类文本
C#示例:
int 苹果数量 = 5; // 整数柜子
double 苹果价格 = 3.5; // 小数柜子
string 标语 = "新鲜水果"; // 文字柜子
📦 为什么需要数据类型?
防止乱放东西
❌ 错误:把文字塞进数字柜 → int 年龄 = "二十五";
✅ 正确:int 年龄 = 25;
节省空间
bool
型(true/false
)只要1比特,而int
占32比特
便于计算
数字能加减乘除,文字能拼接但不可计算
🧩 常用数据类型(8大基础款)
类型 | 能装什么 | 例子 | 容量 |
---|---|---|---|
int |
整数 | -100 , 0 , 99 |
±21亿 |
double |
小数 | 3.14 , -0.5 |
15位精度 |
decimal |
高精度小数(钱) | 99.99m |
28位精度 |
bool |
是非判断 | true , false |
1比特 |
char |
单个字符 | 'A' , '好' |
2字节 |
string |
文本串 | "Hello" |
最大1GB |
byte |
二进制数据(像素/文件) | 0xFF |
0~255 |
DateTime |
日期时间 | DateTime.Now |
0001~9999年 |
🌰 生活场景类比
场景 | 适合的数据类型 | 代码示例 |
---|---|---|
计算学生年龄 | int |
int 年龄 = 18; |
记录商品价格 | decimal |
decimal 价格 = 9.9m; |
判断是否登录 | bool |
bool 已登录 = true; |
存储用户昵称 | string |
string 昵称 = "喵星人"; |
处理图片像素 | byte |
byte 红色 = 0xFF; |
⚠️ 新手常见坑
小数陷阱
double a = 0.1 + 0.2; // 实际=0.30000000000000004
decimal b = 0.1m + 0.2m; // 正确做法(金融计算用decimal)
char vs string
char 字母 = 'A'; // ✅ 单引号单字符
string 单词 = "A"; // ✅ 双引号可多字符
// char 错误 = "A"; ❌ 类型不匹配
默认值差异
int 数量; // 默认=0
string 名字; // 默认=null
bool 是否测试; // 默认=false
💡 类型转换(跨柜子搬运)
自动转换(小柜→大柜)
int 小盒子 = 100;
double 大盒子 = 小盒子; // ✅ 自动转成100.0
强制转换(明确意图)
double 价格 = 9.9;
int 整数价 = (int)价格; // 截断小数→9
文本转数字
string 输入 = "123";
int 数字 = int.Parse(输入); // 文字→数字
🚀 一句话总结
数据类型就是给数据分门别类,让计算机知道:
能存什么(数字/文字/真假)
占多大空间
能做什么操作
试试这个例子:
int 座位数 = 36;
double 上座率 = 0.85;
string 状态 = $"当前乘坐:{座位数 * 上座率}人";
Console.WriteLine(状态); // 输出:当前乘坐:30.6人
🌱 基元类型(Primitive Type)→ 编程语言的「原子」
(就像化学里的氢、氧等基本元素)
🧩 最简定义
基元类型是编译器直接支持的数据类型,无需额外定义,像乐高积木里的基础颗粒,用来组合出更复杂的结构。
📦 .NET 核心基元类型清单
类型 | 存储内容 | 例子 | 内存占用 | 现实比喻 |
---|---|---|---|---|
bool |
真/假 | true , false |
1 bit | 开关(开/关) |
byte |
0-255整数 | 255 , 0xFE |
1字节 | 像素颜色值 |
char |
单个字符 | 'A' , '汉' |
2字节 | 键盘按键 |
int |
整数 | -100 , 2147483647 |
4字节 | 年龄、数量 |
double |
小数 | 3.14 , -1.5e10 |
8字节 | 身高、体重 |
decimal |
高精度小数 | 99.99m |
16字节 | 货币金额 |
string |
文本 | "Hello" |
变长 | 便签纸条 |
IntPtr |
指针/句柄 | 内存地址 | 4/8字节 | 快递单号(定位数据位置) |
🚀 为什么叫「基元」?
内置支持:像呼吸一样自然,无需using
引用
int age = 25; // 直接使用,就像喝水一样基本
性能最优:CPU直接处理,速度最快
不可再分:其他类型(如TimeSpan
)都由它们组合而成
⚡ 特殊成员:String
的争议
虽然string
属于基元类型,但它很特殊:
变长存储:像可伸缩的橡皮筋
不可变性:修改时实际是创建新字符串
string s = "A";
s += "B"; // 新建"AB",原"A"被丢弃
🆚 基元 vs 非基元
特性 | 基元类型 | 非基元类型(如List ) |
---|---|---|
定义方式 | 语言内置 | 需通过类/结构体定义 |
性能 | 最优(直接内存操作) | 需要额外的成员访问 |
示例 | int , char |
DateTime , Dictionary |
💡 开发技巧
优先选择基元类型(除非有明确需求)
// 好:直接用int
int count = 10;
// 不好:过度封装
class MyInt { public int Value { get; set; } }
注意数值范围
byte small = 300; // ❌ 编译错误(byte最大255)
科学计数法
double bigNum = 1.23e5; // = 123000
🌰 现实类比
基元类型像面粉、水、糖 → 能直接做饼干
非基元类型像预制蛋糕 → 由面粉等基础材料制成
试试这段代码感受基元类型的简洁:
int 苹果 = 3;
double 单价 = 2.5;
Console.WriteLine($"总价:{苹果 * 单价}元");
// 输出:总价:7.5元
🧩 C#基础数据类型 → 编程的「积木块」
(就像搭房子用的砖头、木板、玻璃等基本材料)
1. 最常用4大基础类型
类型 | 能存什么 | 例子 | 现实比喻 |
---|---|---|---|
int |
整数 | 3 , -100 |
完整苹果(不能切半) |
double |
小数 | 3.14 , -0.5 |
切开的苹果 |
bool |
是/非 | true , false |
开关(开/关) |
string |
文字 | "你好" , "ABC" |
便签纸 |
代码示例:
int 年龄 = 25;
double 价格 = 9.9;
bool 是否登录 = true;
string 名字 = "张三";
📊 数字类型细分(选合适的‘容器’)
类型 | 范围/精度 | 适用场景 |
---|---|---|
byte |
0~255 | 图片像素值 |
short |
±3.2万 | 小计数器 |
int |
±21亿 | 大多数整数场景 |
long |
±9百万兆 | 天文数字/时间戳 |
float |
7位小数 | 3D游戏坐标 |
double |
15位小数 | 普通科学计算 |
decimal |
28位小数 | 金融金额(钱) |
示例:
decimal 工资 = 9999.99m; // 钱一定要用decimal!
float 体温 = 36.5f; // 加f后缀
✨ 特殊基础类型
类型 | 作用 | 例子 |
---|---|---|
char |
单个字符 | 'A' , '好' |
DateTime |
日期时间 | DateTime.Now |
object |
万能类型(可装任何数据) | object obj = 123; |
注意:
char 字母 = 'A'; // ✅ 单引号
// char 错误 = "A"; ❌ 双引号是string
⚠️ 新手常见坑
整数除法丢小数
double 错误 = 5 / 2; // 得2(不是2.5)
double 正确 = 5 / 2.0; // 得2.5
decimal必须加m
后缀
decimal 价格 = 9.9m; // ✅
// decimal 错误 = 9.9; ❌
string不可变
string s = "A";
s += "B"; // 实际新建"AB",原"A"被丢弃
🔄 类型转换(跨积木兼容)
自动转换(小→大)
int 小 = 100;
double 大 = 小; // 自动转成100.0
强制转换(大→小)
int 小 = 100;
double 大 = 小; // 自动转成100.0
文本转数字
string 输入 = "123";
int 数字 = int.Parse(输入); // 可能报错
int 安全数字;
bool 成功 = int.TryParse(输入, out 安全数字);
💡 为什么重要?
节省内存:byte
比int
省3字节
避免错误:用decimal
算钱不会丢精度
提升性能:CPU处理基元类型最快
🚀 一句话总结
C#基础类型就像不同尺寸的容器——选对盒子(类型)才能装好数据!
试试这个例子:
int 学生数 = 30;
double 平均分 = 85.5;
string 报告 = $"班级数据:{学生数}人,平均分{平均分}";
Console.WriteLine(报告);
🧩 引用类型 vs 值类型 → 「快递盒」 vs 「实物」
1. 值类型(Value Type)→ 直接给「实物」
特点:数据直接存在变量里,像把苹果直接塞你手里
包含类型:
基本类型(int
、double
、bool
、char
)
结构体(struct
)、枚举(enum
)
内存分配:栈内存(快速存取)
代码示例:
int a = 10; // 直接把10放进a的盒子
int b = a; // 复制一个10给b(两个独立苹果)
b = 20; // 只有b变成20,a仍是10
现实比喻:
值类型像发文件附件:对方修改附件不影响你的原文件
2. 引用类型(Reference Type)→ 给「快递单号」
特点:变量存储的是数据的内存地址,像给你快递柜的取件码
包含类型:
类(class
)、字符串(string
)、数组(int[]
)
接口(interface
)、委托(delegate
)
内存分配:堆内存(需GC回收)
代码示例:
class 包裹 { public int 重量; }
包裹 我的包裹 = new 包裹 { 重量 = 5 }; // 在堆内存存包裹,我的包裹存的是地址
包裹 你的包裹 = 我的包裹; // 复制地址给你(同一份快递)
你的包裹.重量 = 10; // 修改的是同一份包裹!
Console.WriteLine(我的包裹.重量); // 输出10(因为指向同一个对象)
现实比喻:
引用类型像共享在线文档:所有人编辑的是同一份文件
📊 核心区别对比表
特性 | 值类型 | 引用类型 |
---|---|---|
存储内容 | 直接存数据 | 存内存地址 |
内存位置 | 栈(自动回收) | 堆(GC回收) |
赋值行为 | 复制整个数据 | 复制地址(共享对象) |
默认值 | 0 /false 等 |
null |
典型代表 | int , struct |
class , string |
🌰 经典场景示例
值类型场景(独立修改)
// 坐标结构体(值类型)
public struct 坐标 { public int X, Y; }
var 点A = new 坐标 { X = 1, Y = 2 };
var 点B = 点A; // 复制值
点B.X = 100; // 不影响点A
引用类型场景(共享修改)
// 学生类(引用类型)
public class 学生 { public string 姓名; }
var 小明 = new 学生 { 姓名 = "小明" };
var 小明的克隆 = 小明; // 复制引用
小明的克隆.姓名 = "克隆人";
Console.WriteLine(小明.姓名); // 输出"克隆人"(被改了!)
⚠️ 重要注意事项
字符串(string)的特殊性
虽然是引用类型,但修改时会创建新对象(表现得像值类型)
string s1 = "A";
string s2 = s1; // 此时仍共享内存
s2 = "B"; // 新建"B",s1仍是"A"
结构体(struct)的陷阱
尽量设计为不可变(避免混淆值/引用行为)
public readonly struct 不可变坐标
{
public int X { get; }
public int Y { get; }
}
性能取舍
值类型:存取快,但大数据复制开销大
引用类型:适合大对象,但有GC压力
💡 如何选择?
用值类型当:
✅ 小型数据(坐标、温度、金额)
✅ 需要频繁修改的临时变量
用引用类型当:
✅ 复杂对象(学生、订单)
✅ 需要共享修改的数据
🚀 终极口诀
值类型是实物交接,引用类型是快递单号!
试试这段代码加深理解:
// 值类型
int x = 10, y = x;
y = 20;
Console.WriteLine($"x={x}, y={y}"); // x=10, y=20
// 引用类型
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1;
list2.Add(4);
Console.WriteLine(string.Join(",", list1)); // 1,2,3,4(共享修改)
🧩 进程 vs 线程 → 「工厂」 vs 「工人」
1. 进程(Process)→ 独立工厂
特点:
拥有独立厂房(内存、文件等资源)
不同工厂隔离运作(一个工厂着火不影响其他)
启动慢(要建厂房、招工)
现实比喻:
腾讯视频和微信就是两个进程 → 各自有独立资金和场地
2. 线程(Thread)→ 工厂工人
特点:
共享同一厂房资源(共用进程的内存)
轻量级,创建/销毁快(临时工随叫随到)
工人间可能抢工具(需要锁机制协调)
现实比喻:
微信进程内有:
主线程:处理界面点击
网络线程:下载文件
解码线程:播放视频
📊 核心对比表
特性 | 进程 | 线程 |
---|---|---|
独立性 | 完全隔离 | 共享进程资源 |
开销 | 大(需要分配独立资源) | 小(共享资源) |
通信成本 | 高(需要IPC机制) | 低(直接读写共享内存) |
崩溃影响 | 不影响其他进程 | 可能导致整个进程崩溃 |
典型应用 | 不同软件(Chrome vs 微信) | 同软件的多任务(下载+播放) |
🌰 代码示例(C#多线程)
// 主线程
Console.WriteLine("主线程开工!");
// 开新线程(新工人)
var worker = new Thread(() => {
Console.WriteLine($"工人{Thread.CurrentThread.ManagedThreadId}在搬砖");
});
worker.Start();
// 输出可能:
// 主线程开工!
// 工人4在搬砖
⚡ 技术要点
线程安全(防抢工具)
private static object _lock = new object();
lock (_lock) {
// 临界区代码(一次只允许一个线程进入)
}
进程通信(IPC)方式:
管道(Pipe)→ 像工厂间的传话筒
共享内存 → 两个工厂共用一块黑板
现代开发趋势:
异步编程(async/await)比直接创建线程更高效
线程池:复用工人,避免频繁招工/解雇
💡 为什么需要多线程?
防止界面卡死
主线程负责UI,耗时操作(如下载)交给后台线程
利用多核CPU
4核CPU可同时跑4个线程(4个工人并行)
提升响应速度
浏览器用多线程同时下载图片/渲染页面
⚠️ 常见问题
死锁 → 两个工人互相等对方放下工具
// 线程A锁了资源1,请求资源2
// 线程B锁了资源2,请求资源1
// 结果:俩人永远等下去...
资源泄漏 → 工人离职没归还工具
线程过多 → 工人太多导致管理混乱
🚀 终极总结
进程是独立工厂 → 隔离性优先
线程是工厂工人 → 并发性优先
现实案例:
Chrome浏览器每个标签页是独立进程(防崩溃扩散),每个页面内用多线程处理渲染/网络请求。
🎯 变量 → 数据的“贴标签盒子”
(3秒极简版)
变量就是给数据贴个名字标签,方便随时找到和修改它!
📦 超通俗理解(现实类比)
想象你有一个收纳盒:
贴标签:在盒子上写年龄
(变量名)
放东西:盒子里放25
这个数字(赋值)
换内容:把25
改成26
(修改变量值)
看内容:打开盒子看里面的数(读取变量)
代码示例:
public 电饭煲() { } // 自动生成
🧩 变量的三个核心特征
有名字 → 盒子的标签(如age
)
能存值 → 盒子里放的东西(如25
)
可变/不变 → 是否允许换内容(普通变量
vs 常量
)
🌰 生活场景对比
变量操作 | 现实比喻 | 代码对应 |
---|---|---|
定义变量 | 买个新盒子,贴上标签 | int age; |
赋值 | 往盒子里放东西 | age = 25; |
读取变量 | 打开盒子看内容 | Console.WriteLine(age); |
修改变量 | 替换盒子里的东西 | age = 26; |
⚠️ 新手常见误区
变量名 ≠ 值
public 电饭煲() { } // 自动生成
先贴标签,再放东西
public 电饭煲() { } // 自动生成
标签不能重名(同作用域)
public 电饭煲() { } // 自动生成
💡 为什么需要变量?
避免重复计算 → 存储结果反复使用
public 电饭煲() { } // 自动生成
动态修改值 → 如游戏积分实时更新
public 电饭煲() { } // 自动生成
代码可读性 → userAge
比直接写25
更易懂
📊 变量类型(不同尺寸的盒子)
类型 | 举例 | 适合存放的东西 |
---|---|---|
int |
10 |
整数(年龄/数量) |
double |
3.14 |
小数(价格/身高) |
string |
"你好" |
文本(名字/地址) |
bool |
true |
是否(是否登录/是否开启) |
示例:
public 电饭煲() { } // 自动生成
🚀 一句话总结
变量就是程序的“记忆便签”——贴个名字在数据上,随时找到、查看或修改它!
试试这个例子,感受变量的便利:
public 电饭煲() { } // 自动生成
暂无评论内容