2.1 为什么需要变量?

2.1.1 一个没有变量的程序能做什么?

csharp

Console.WriteLine("1 + 1 = 2");
Console.WriteLine("1 + 2 = 3");
Console.WriteLine("2 + 3 = 5");

问题:如果想把所有"1"改成"2",需要改 5 个地方(而且容易漏掉)。

2.1.2 有变量的程序

csharp

int a = 1;
int b = 1;
Console.WriteLine($"{a} + {b} = {a + b}");

// 如果想改成 2 + 2,只需要改前两行

变量的本质

变量 = 一个带名字的盒子,用来存放数据

text

内存示意图:

     ┌─────────┐
name │  "张三"  │  ← string 类型的变量,存文本
     └─────────┘
     
     ┌─────────┐
age  │   25    │  ← int 类型的变量,存整数
     └─────────┘
     
     ┌─────────┐
price│  19.99  │  ← double 类型的变量,存小数
     └─────────┘

2.2 C# 中的数据类型全家福

2.2.1 数据类型的分类

text

C# 数据类型
├── 值类型 (Value Types)
│   ├── 整数类型 (sbyte, byte, short, ushort, int, uint, long, ulong)
│   ├── 浮点类型 (float, double, decimal)
│   ├── 字符类型 (char)
│   ├── 布尔类型 (bool)
│   └── 结构体/枚举 (后续章节)
│
└── 引用类型 (Reference Types)
    ├── 字符串 (string)
    ├── 数组 (array)
    ├── 类 (class)
    └── 接口/委托 (后续章节)

2.2.2 值类型 vs 引用类型(直观理解)

特点 值类型 引用类型
数据存在哪 直接存在变量里 变量存的是"地址",数据在别处
赋值时 复制整个数据 复制地址(两个变量指着同一个东西)
典型例子 int, double, bool string, 数组, 类

一个帮助理解的类比

text

值类型 = 你自己拿着现金
- 你借给朋友100元,朋友拿到的是另一张100元
- 你改自己手里的钱,不影响朋友

引用类型 = 你告诉朋友钱在哪个银行账户
- 你给朋友的是账号密码
- 你和朋友操作的是同一个账户
- 你改了余额,朋友看到的也变了

初学者先知道有这个区别就行,后面会深入讲解。

2.2.3 整数类型(重点)

为什么需要这么多种整数?

因为不同的整数占用不同大小的内存,选合适的可以节省内存

类型 中文名 大小 范围 什么时候用
byte 字节 1 字节 0 到 255 年龄、分数 (0-100)
sbyte 有符号字节 1 字节 -128 到 127 小范围有正负的值
short 短整数 2 字节 -32,768 到 32,767 较小的整数
ushort 无符号短整数 2 字节 0 到 65,535 小范围非负数
int 整数 4 字节 -21亿 到 21亿 最常用! 日常整数
uint 无符号整数 4 字节 0 到 42亿 非负整数
long 长整数 8 字节 -9百亿亿 到 9百亿亿 超大数字(如宇宙年龄)
ulong 无符号长整数 8 字节 0 到 18百亿亿 超大非负数字

代码示例

csharp

byte myAge = 25;           // ✅ 25 在 0-255 范围内
byte myAge2 = 256;         // ❌ 编译错误!256 超出范围

int population = 1400000000;   // 14亿,int 足够
long universeAge = 13800000000; // 138亿年,必须用 long
选择建议

csharp

// 99% 的情况,直接用 int
int score = 100;
int count = 5;
int year = 2024;

// 只有明确需要时才用其他类型
byte smallValue = 255;      // 比如 RGB 颜色值 (0-255)
long bigNumber = 9999999999; // 超过 21 亿时才用

2.2.4 浮点类型(小数)

类型 中文名 大小 精度 什么时候用
float 单精度浮点数 4 字节 约 6-9 位有效数字 图形、游戏(对精度要求不高)
double 双精度浮点数 8 字节 约 15-17 位有效数字 最常用! 科学计算、普通小数
decimal 十进制浮点数 16 字节 28-29 位有效数字 金融、货币(必须精确)

重要区别

csharp

// float 类型需要在数字后加 f 或 F
float height = 1.75f;      // 不加 f 会报错

// double 是默认的小数类型
double pi = 3.1415926535;   // 不需要后缀

// decimal 需要加 m 或 M
decimal price = 99.99m;     // 货币用 decimal

为什么不能都用 double?

csharp

double a = 0.1;
double b = 0.2;
double c = a + b;
Console.WriteLine(c);  
// 输出:0.30000000000000004  (不是精确的 0.3!)

decimal d = 0.1m;
decimal e = 0.2m;
decimal f = d + e;
Console.WriteLine(f);  
// 输出:0.3  (精确!)

解释

  • double 用二进制存储小数,有些十进制小数无法精确表示(0.1 在二进制是无限循环)

  • decimal 用十进制方式存储,精确但速度较慢、占用更多内存

选择规则

text

金额、价格、任何需要精确计算 → decimal
科学计算、游戏物理、图形 → double
性能极致优化、大量数据 → float

2.2.5 字符类型 (char)

char 存储单个字符,用单引号

csharp

char grade = 'A';        // 单个字母
char digit = '5';        // 数字字符(不是数字5)
char symbol = '$';       // 符号
char chinese = '中';     // 中文也可以
char newline = '\n';     // 转义字符(换行)

// 错误写法
char wrong = "A";        // ❌ 双引号是字符串,不是字符
char wrong2 = 'AB';      // ❌ 只能一个字符

char 的本质:字符在计算机里实际上是一个数字(Unicode 编码)

csharp

char a = 'A';
Console.WriteLine((int)a);  // 输出:65(A 的编码)

char b = '中';
Console.WriteLine((int)b);  // 输出:20013('中' 的编码)

// 也可以通过编码值得到字符
char c = (char)65;
Console.WriteLine(c);       // 输出:A

2.2.6 布尔类型 (bool)

bool 只有两个值:true(真)或 false(假)

csharp

bool isRaining = true;      // 正在下雨吗?是的
bool isStudent = false;     // 是学生吗?不是
bool isGreater = 5 > 3;     // 5>3 是成立的,所以 isGreater = true

Console.WriteLine(isRaining);   // 输出:True
Console.WriteLine(isGreater);   // 输出:True

布尔值的应用

  • 条件判断(if 语句,第三章会讲)

  • 循环控制(while 循环)

  • 开关标志(是否启用某功能)

2.2.7 字符串类型 (string)

string 存储一串字符(文本),用双引号

csharp

string name = "张三";
string message = "Hello, World!";
string empty = "";           // 空字符串
string space = " ";          // 包含一个空格的字符串
string number = "123";       // 字符串形式的数字,不能直接做加法

// 字符串拼接
string firstName = "张";
string lastName = "三";
string fullName = firstName + lastName;  // "张三"

// 字符串内插(推荐方式)
int age = 25;
string intro = $"我叫{name},今年{age}岁";
Console.WriteLine(intro);    // 输出:我叫张三,今年25岁

常见的字符串操作

csharp

string text = "Hello C#";

Console.WriteLine(text.Length);        // 获取长度:8(空格也算)
Console.WriteLine(text.ToUpper());     // 转大写:HELLO C#
Console.WriteLine(text.ToLower());     // 转小写:hello c#
Console.WriteLine(text.Substring(6));  // 从第6个字符开始:C#
Console.WriteLine(text.Contains("C#")); // 是否包含:True
Console.WriteLine(text.Replace("C#", "World")); // 替换:Hello World

转义字符(在字符串里表示特殊符号):

转义序列 含义 示例
\n 换行 "第一行\n第二行"
\t 制表符(Tab) "姓名\t年龄"
\\ 反斜杠本身 "C:\\Windows"
\" 双引号本身 "他说\"你好\""
\' 单引号本身 "\'"

csharp

// 原样输出
Console.WriteLine("第一行\n第二行");
// 输出:
// 第一行
// 第二行

Console.WriteLine("C:\\Program Files");
// 输出:C:\Program Files

Console.WriteLine("他说:\"你好\"");
// 输出:他说:"你好"

// 逐字字符串(@ 符号,不用转义)
Console.WriteLine(@"C:\Program Files\MyApp");
Console.WriteLine(@"第一行
第二行
第三行");

2.3 变量的声明、赋值和使用

2.3.1 声明变量的语法

csharp

// 语法:类型 变量名;

int age;           // 声明一个 int 类型的变量,名字叫 age
string name;       // 声明一个 string 类型的变量
bool isActive;     // 声明一个 bool 类型的变量

2.3.2 赋值

csharp

int age;           // 先声明
age = 25;          // 再赋值

string name;
name = "张三";

// 声明时直接赋值(最常用)
int score = 100;
double price = 19.99;
bool isValid = true;

2.3.3 使用变量

csharp

int a = 5;
int b = 3;
int sum = a + b;           // 用 a 和 b 计算,结果存到 sum

Console.WriteLine(a);      // 输出 a 的值
Console.WriteLine(b);
Console.WriteLine(sum);

// 变量可以参与表达式
int result = (a + b) * 2 - a;

2.3.4 变量的重新赋值(重要!)

csharp

int count = 10;      // 初始值 10
Console.WriteLine(count);  // 10

count = 20;          // 重新赋值,覆盖旧值
Console.WriteLine(count);  // 20

count = count + 5;   // 先读取 count 的值(20),加5得25,再存回 count
Console.WriteLine(count);  // 25

2.3.5 变量的命名规则(必读!)

硬性规则(编译器强制):

  1. 只能由字母、数字、下划线 _ 组成

  2. 第一个字符不能是数字

  3. 不能是 C# 关键字(如 int、class、public 等)

关键字列表(这些不能用作变量名):

text

abstract, as, base, bool, break, byte, case, catch, char, checked,
class, const, continue, decimal, default, delegate, do, double, else,
enum, event, explicit, extern, false, finally, fixed, float, for,
foreach, goto, if, implicit, in, int, interface, internal, is, lock,
long, namespace, new, null, object, operator, out, override, params,
private, protected, public, readonly, ref, return, sbyte, sealed,
short, sizeof, stackalloc, static, string, struct, switch, this,
throw, true, try, typeof, uint, ulong, unchecked, unsafe, ushort,
using, virtual, void, volatile, while

命名规范(强烈建议遵守):

命名规范 规则 示例 适用场景
camelCase 首字母小写,后续单词首字母大写 myAgestudentNametotalScore 变量、方法参数
PascalCase 每个单词首字母大写 StudentNameCalculateScore 类名、方法名、属性
UPPER_CASE 全大写,下划线分隔 MAX_COUNTPI_VALUE 常量

csharp

// ✅ 正确的变量名
int age = 25;
double studentScore = 95.5;
string firstName = "张";
int _tempValue = 100;        // 下划线开头也是允许的
int count1 = 10;             // 数字在末尾

// ❌ 错误的变量名
int 123abc = 10;             // 不能数字开头
string my-name = "张三";      // 不能有横线
double class = 3.14;         // class 是关键字
string 姓名 = "张三";         // 虽然中文可以(不推荐!)

专业建议:始终使用有意义的英文变量名,不要用拼音或中文。

2.3.6 变量的作用域(初步了解)

作用域 = 变量存在的区域,出了这个区域变量就"消失"了

csharp

// 大括号 { } 定义了一个作用域
{
    int x = 10;    // x 只能在这个大括号里使用
    Console.WriteLine(x);  // ✅ 可以
}
Console.WriteLine(x);  // ❌ 错误!x 已经不存在了

更实际的例子

csharp

int globalVar = 100;  // 在 Main 方法中声明的变量

{
    int localVar = 50;        // 只能在内部
    Console.WriteLine(globalVar);  // ✅ 可以访问外部的
    Console.WriteLine(localVar);   // ✅ 可以
}

Console.WriteLine(globalVar);  // ✅ 可以
Console.WriteLine(localVar);   // ❌ 错误!localVar 已经没了

2.4 常量的使用

常量 = 值永远不会改变的量

2.4.1 为什么需要常量?

csharp

// 不好的写法:魔法数字
double area = 3.14159 * radius * radius;  // 3.14159 是什么?

// 好的写法:使用常量
const double PI = 3.1415926535;
double area = PI * radius * radius;

2.4.2 常量的声明和使用

csharp

const double PI = 3.14159;
const int DAYS_IN_WEEK = 7;
const string APP_NAME = "我的计算器";

// 使用常量
Console.WriteLine(APP_NAME);
double circleArea = PI * 5 * 5;

PI = 3.14;  // ❌ 错误!常量不能重新赋值

常量的命名规范:全部大写,单词间用下划线分隔

2.4.3 const vs readonly(简略介绍)

  • const:编译时常量,值在编译时就确定了

  • readonly:运行时常量,可以在构造函数中赋值(后面会讲)


2.5 类型转换

2.5.1 为什么需要类型转换?

csharp

int intNumber = 10;
double doubleNumber = intNumber;  // ✅ 自动转换(int 到 double)

double pi = 3.14;
int intPi = pi;                   // ❌ 编译错误!不能自动转

2.5.2 隐式转换(自动转换)

小范围类型 → 大范围类型,自动发生,不会丢失数据

text

byte → short → int → long → float → double
          ↑                    ↗
         char ─────────────────┘

csharp

byte b = 100;
int i = b;           // ✅ byte 自动转 int
long l = i;          // ✅ int 自动转 long
float f = l;         // ✅ long 自动转 float
double d = f;        // ✅ float 自动转 double

char c = 'A';
int code = c;        // ✅ char 自动转 int(得到 65)

2.5.3 显式转换(强制转换)

大范围类型 → 小范围类型,需要手动指定,可能丢失数据

语法(目标类型)要转换的值

csharp

double pi = 3.14159;
int intPi = (int)pi;        // 强制转换,小数部分被截断
Console.WriteLine(intPi);   // 输出:3(不是四舍五入,是直接舍去)

long bigNumber = 10000000000;
int smallNumber = (int)bigNumber;  // 危险!数据溢出
Console.WriteLine(smallNumber);     // 输出:1410065408(错误的结果)

// char 和 int 互转
int code = 65;
char letter = (char)code;
Console.WriteLine(letter);   // 输出:A

溢出问题

csharp

// int 最大值约 21 亿
int maxInt = 2147483647;
int overflow = maxInt + 1;   // 会溢出,得到 -2147483648

// 使用 checked 检测溢出
checked
{
    int willOverflow = 2147483647 + 1;  // 抛出 OverflowException
}

2.5.4 Convert 类的转换

Convert 提供各种类型之间的转换,适合字符串转数字

csharp

string numStr = "123";
int num = Convert.ToInt32(numStr);    // 字符串 "123" → 整数 123

string piStr = "3.14";
double pi = Convert.ToDouble(piStr);   // 字符串 → double

string boolStr = "true";
bool flag = Convert.ToBoolean(boolStr); // "true" → true

// 其他常用方法
Convert.ToString(123);     // 数字 → 字符串
Convert.ToByte("255");     // 字符串 → byte
Convert.ToChar("A");       // 字符串 → char

Convert vs 强制转换的区别

csharp

// 强制转换 (int):直接截断小数
double d = 3.99;
int a = (int)d;           // 结果:3

// Convert.ToInt32:四舍五入
int b = Convert.ToInt32(d);  // 结果:4

// 字符串转换只能用 Convert 或 Parse
string s = "123";
int c = (int)s;           // ❌ 编译错误!不能直接转
int d = Convert.ToInt32(s); // ✅ 正确
int e = int.Parse(s);     // ✅ 也可以用 Parse 方法

2.5.5 Parse 和 TryParse

csharp

// Parse:如果格式错误会抛出异常
string input = "123";
int number = int.Parse(input);     // 成功

string badInput = "abc";
int badNumber = int.Parse(badInput); // 抛出 FormatException

// TryParse:不会抛出异常,返回 bool 表示是否成功
string userInput = Console.ReadLine();
if (int.TryParse(userInput, out int result))
{
    Console.WriteLine($"转换成功:{result}");
}
else
{
    Console.WriteLine("请输入有效的数字!");
}

2.6 var 隐式类型

var 让编译器自动推断变量类型

csharp

var age = 25;        // 编译器推断为 int
var name = "张三";    // 推断为 string
var pi = 3.14;       // 推断为 double
var isValid = true;  // 推断为 bool

// 等价于
int age = 25;
string name = "张三";
double pi = 3.14;
bool isValid = true;

var 的限制

csharp

var x;              // ❌ 错误!必须初始化
var y = null;       // ❌ 错误!不能推断为 null
var z = {1,2,3};    // ❌ 错误!复杂类型需要明确

什么时候用 var?

csharp

// ✅ 适合:类型明显从右边可以看出
var count = 10;                    // 明显是 int
var name = Console.ReadLine();     // 明显是 string

// ❌ 不适合:类型不明显
var data = GetData();              // 不知道 GetData 返回什么类型
var result = someMethod();         // 降低可读性

团队编码规范:写教程时为了清晰会明确写出类型,日常开发中可以合理使用 var。


2.7 综合示例

示例1:个人信息收集器

csharp

using System;

Console.WriteLine("=== 个人信息收集器 ===");

// 获取输入
Console.Write("请输入姓名:");
string name = Console.ReadLine();

Console.Write("请输入年龄:");
int age = Convert.ToInt32(Console.ReadLine());

Console.Write("请输入身高(米):");
double height = Convert.ToDouble(Console.ReadLine());

Console.Write("请输入体重(公斤):");
double weight = Convert.ToDouble(Console.ReadLine());

Console.Write("是否是学生(true/false):");
bool isStudent = Convert.ToBoolean(Console.ReadLine());

// 计算 BMI
double bmi = weight / (height * height);

// 输出结果
Console.WriteLine("\n=== 您的信息 ===");
Console.WriteLine($"姓名:{name}");
Console.WriteLine($"年龄:{age}");
Console.WriteLine($"身高:{height} 米");
Console.WriteLine($"体重:{weight} 公斤");
Console.WriteLine($"BMI:{bmi:F2}");  // :F2 表示小数点后2位
Console.WriteLine($"是否学生:{isStudent}");

// 年龄判断
if (age >= 18)
{
    Console.WriteLine("您是成年人");
}
else
{
    Console.WriteLine("您是未成年人");
}

示例2:简单的计算器(整数)

csharp

using System;

Console.WriteLine("=== 简单整数计算器 ===");

Console.Write("请输入第一个整数:");
int num1 = Convert.ToInt32(Console.ReadLine());

Console.Write("请输入第二个整数:");
int num2 = Convert.ToInt32(Console.ReadLine());

int sum = num1 + num2;
int diff = num1 - num2;
int product = num1 * num2;
int quotient = num1 / num2;  // 整数除法,只保留整数部分
int remainder = num1 % num2; // 取余数

Console.WriteLine($"{num1} + {num2} = {sum}");
Console.WriteLine($"{num1} - {num2} = {diff}");
Console.WriteLine($"{num1} × {num2} = {product}");
Console.WriteLine($"{num1} ÷ {num2} = {quotient} 余 {remainder}");

2.8 常见错误与陷阱

错误1:未初始化就使用

csharp

int age;
Console.WriteLine(age);  // ❌ 编译错误:使用了未赋值的局部变量

错误2:类型不匹配

csharp

int number = "123";      // ❌ 不能直接把 string 赋给 int
int number = (int)"123"; // ❌ 也不能强制转换
int number = Convert.ToInt32("123"); // ✅ 正确

错误3:整数除法陷阱

csharp

int a = 5;
int b = 2;
double c = a / b;    // 结果:2.0,不是 2.5!
// 原因:a / b 是整数除法,先得到 2,再转成 double

double d = 5.0 / 2;  // 结果:2.5 ✅
double e = (double)a / b; // 结果:2.5 ✅

错误4:溢出没有察觉

csharp

byte b = 255;
b = b + 1;           // 溢出,b 变成 0(不报错)
Console.WriteLine(b); // 输出:0

// 使用 checked 检测
byte c = 255;
c = checked((byte)(c + 1)); // 抛出异常

错误5:字符串拼接 vs 加法

csharp

Console.WriteLine(1 + 2 + "3");   // 输出:33(先算 1+2=3,再拼"3")
Console.WriteLine("1" + 2 + 3);   // 输出:123(字符串拼接,全部变字符串)
Console.WriteLine("1" + (2 + 3));  // 输出:15(先算括号里的 2+3=5)

2.9 本章总结

核心知识点回顾

知识点 要点
变量 存储数据的容器,声明后可用
整数类型 最常用 int,超范围用 long
浮点类型 普通用 double,金额用 decimal
bool 只有 true/false
char 单引号,单个字符
string 双引号,字符串,可用 $ 内插
常量 const,值不可变
隐式转换 小→大,自动
显式转换 大→小,强转,可能丢精度
Convert 字符串↔数字
var 让编译器推断类型

数据类型选择速查表

text

存储年龄      → int (或 byte)
存储分数      → double
存储金额      → decimal
存储开关      → bool
存储单个字符  → char
存储名字      → string
存储大整数    → long

2.10 练习题

基础题

  1. 声明一个 int 变量并赋值为你的出生年份,再声明一个 int 变量为当前年份,计算并输出你的年龄。

  2. 声明一个 double 变量表示温度(摄氏度),转换为华氏度(公式:°F = °C × 9/5 + 32),输出结果。

  3. 交换两个变量的值:

csharp

int a = 10;
int b = 20;
// 在这里写代码,交换后 a=20, b=10

提高题

  1. 用户输入圆的半径,计算并输出:

    • 直径 = 2r

    • 周长 = 2πr

    • 面积 = πr²
      (π 用常量定义,保留2位小数)

  2. 用户输入一个三位整数,分别输出它的百位、十位、个位。

    • 输入:456

    • 输出:百位=4,十位=5,个位=6

    • 提示:用 / 和 % 运算

  3. 用户输入秒数(如 3665),输出转换后的时:分:秒格式(1:1:5)。

挑战题

  1. 写一个程序:

    • 提示用户输入一个字符串

    • 输出字符串的长度

    • 输出字符串的第一个字符

    • 输出字符串的最后一个字符

    • 输出字符串反转后的结果(提示:需要循环或用后面要学的知识)

  2. 数据类型大小探索:

csharp

// 使用 sizeof 可以知道类型占用多少字节
Console.WriteLine($"int 占用 {sizeof(int)} 字节");
// 尝试输出其他类型的大小

更多推荐