3、C#语言基础(上):变量与数据类型、常量、类型转换、运算符与表达式
好,咱们正式开始写代码了。C# 这门语言,说白了就是围绕「数据」转的。你写任何程序,本质上都是在处理数据——存起来、算一算、再输出。所以这一章,咱们先把数据的基本玩法搞清楚。
3.1 变量:数据的容器
变量是什么?你可以把它想象成一个带标签的盒子。盒子里能装东西,标签上写着盒子的名字。在 C# 里,用变量之前必须先声明——告诉编译器:「嘿,我要一个盒子,叫 age,里面放整数。」
int age; // 声明一个整数变量
age = 25; // 赋值
int score = 90; // 声明并初始化,一步到位
我个人习惯在声明变量时就给个初始值。为什么?因为 C# 不允许使用未赋值的局部变量。你想想看,要是你打开一个空盒子,里面有什么?鬼知道。编译器干脆就不让你用,省得出 bug。
myAge、studentName。别用拼音,也别用 a、b、c 这种无意义的名字。我在项目中见过有人用 aaa、bbb 命名,三个月后他自己都看不懂了。
3.2 数据类型:盒子的大小和形状
C# 是强类型语言。什么意思?每个变量都有固定的类型,不能今天装整数,明天装字符串。类型决定了数据在内存里占多大空间、能做什么操作。
3.2.1 值类型 vs 引用类型
这是 C# 里一个很重要的概念。值类型直接存数据,引用类型存的是数据的地址。打个比方:值类型就像你直接把钱揣兜里,引用类型就像你把钱存银行,兜里只放一张银行卡。
| 类别 | 常见类型 | 存储位置 |
|---|---|---|
| 值类型 | int, double, bool, char, struct | 栈(Stack) |
| 引用类型 | string, class, array, object | 堆(Heap) |
3.2.2 常用数据类型一览
咱们挑几个最常用的说说:
- int:32 位整数,范围 ±21 亿。够用 99% 的场景。
- long:64 位整数,数星星都够了。
- float / double:浮点数。float 精度 7 位,double 15 位。我一般直接用 double,省心。
- decimal:高精度小数,适合金融计算。记得加
m后缀:decimal price = 19.99m; - bool:只有 true 和 false。别写
if (isOK == true),直接if (isOK)就行。 - string:字符串。注意它是引用类型,但行为上有点像值类型,面试常考这个。
- char:单个字符,用单引号:
'A'。
3.3 常量:不变的值
有些数据在程序运行期间不能变,比如圆周率、税率、配置参数。这时候就用 const 关键字。
const double PI = 3.1415926;
const int MAX_RETRY_COUNT = 3;
常量有几个特点:声明时必须赋值,之后不能改。而且它是在编译时就确定的,所以不能用在运行时才能知道的值上。如果你需要一个运行时常量,用 readonly 关键字,这个咱们后面再讲。
我个人习惯把常量名全大写,单词间用下划线。这不是 C# 的强制要求,但很多团队都这么约定,一眼就能看出来这是个常量。
3.4 类型转换:数据变形记
实际开发中,你经常需要把一种类型转成另一种。比如用户输入的是字符串,你要转成数字才能做计算。C# 提供了几种转换方式。
3.4.1 隐式转换
从小范围到大范围,编译器自动帮你转。比如 int 转 long、float 转 double。因为不会丢数据,所以放心转。
int i = 100;
long l = i; // 隐式转换,没问题
double d = i; // 也是隐式转换
3.4.2 显式转换(强制转换)
从大范围到小范围,可能丢数据,必须你手动告诉编译器:「我知道风险,我负责。」
double d = 3.14;
int i = (int)d; // i 变成 3,小数部分丢了
嗯,这里要注意:强制转换不会四舍五入,直接截断。如果你需要四舍五入,用 Math.Round()。
3.4.3 Convert 类转换
最常用的转换方式,尤其是字符串转数字的时候。
string input = "123";
int number = Convert.ToInt32(input);
如果字符串格式不对,Convert 会抛出异常。所以实际项目中,我一般配合 try-catch 或者用 int.TryParse() 来安全转换。
Convert.ToInt32() 转换 null 时,会返回 0。而 int.Parse(null) 会直接抛异常。这个区别在面试里经常被问到。
3.4.4 使用 Parse 和 TryParse
// Parse:转换失败就抛异常
int a = int.Parse("123");
// TryParse:转换失败返回 false,不会抛异常
string s = "abc";
if (int.TryParse(s, out int result))
{
Console.WriteLine($"转换成功:{result}");
}
else
{
Console.WriteLine("转换失败");
}
我个人强烈推荐 TryParse。为什么?因为用户输入永远不可信。我曾经在项目中用 Parse,结果用户输了个「十二」,程序直接崩溃。用 TryParse 优雅处理,用户体验好得多。
3.5 运算符与表达式:让数据动起来
有了数据,接下来就是怎么操作它们。运算符就是干这个的。
3.5.1 算术运算符
加减乘除取模,跟数学课上学的差不多。
int a = 10, b = 3;
int sum = a + b; // 13
int diff = a - b; // 7
int product = a * b; // 30
int quotient = a / b; // 3(整数除法,结果取整)
int remainder = a % b; // 1(取模,就是余数)
这里有个坑:两个整数相除,结果还是整数。如果你想要小数结果,至少让其中一个操作数是浮点数。
double result = 10 / 3; // 结果是 3.0,不是 3.333
double correct = 10.0 / 3; // 结果是 3.333...
3.5.2 赋值运算符
除了基本的 =,还有复合赋值运算符,写起来更简洁。
int x = 5;
x += 3; // 等价于 x = x + 3
x -= 2; // 等价于 x = x - 2
x *= 4; // 等价于 x = x * 4
x /= 2; // 等价于 x = x / 2
x %= 3; // 等价于 x = x % 3
3.5.3 比较运算符
比较的结果是 bool 类型,要么 true 要么 false。
int age = 18;
bool isAdult = age >= 18; // true
bool canDrive = age >= 16; // true
bool isEqual = (age == 18); // true,注意是两个等号
新手最容易犯的错误:把 == 写成 =。在 C# 里,if (a = 5) 会编译报错,因为赋值表达式的结果是 int 类型,不能当 bool 用。这一点比 C/C++ 安全多了。
3.5.4 逻辑运算符
用来组合多个条件。
bool hasLicense = true;
bool hasInsurance = false;
// && 与:两个都 true 才 true
bool canRent = hasLicense && hasInsurance; // false
// || 或:一个 true 就 true
bool canDrive = hasLicense || hasInsurance; // true
// ! 非:取反
bool cannotDrive = !canDrive; // false
逻辑运算符有短路特性。比如 A && B,如果 A 是 false,B 根本不会执行。这个特性有时候可以用来做优化,但也要小心别写出依赖副作用的代码。
3.5.5 自增自减运算符
写循环时经常用到。
int count = 0;
count++; // count 变成 1,等价于 count = count + 1
count--; // count 变回 0
// 前缀和后缀的区别
int a = 5;
int b = a++; // b = 5,a = 6(先赋值,后自增)
int c = ++a; // a = 7,c = 7(先自增,后赋值)
我个人建议:除非你很清楚自己在做什么,否则别把自增自减写在复杂表达式里。单独一行用,清晰明了。
3.6 表达式:组合的艺术
把变量、常量和运算符组合起来,就构成了表达式。每个表达式都有一个计算结果,这个结果有类型。
int result = (10 + 5) * 2 - 8 / 4; // 结果是 28
运算符有优先级,跟数学一样:先乘除后加减,括号最优先。记不住优先级怎么办?加括号。括号不仅让代码更清晰,还能避免一些奇怪的 bug。
好了,这一章的内容就这些。变量、数据类型、常量、类型转换、运算符——这些都是 C# 的「砖块」。后面的章节,咱们就用这些砖块搭房子了。下一章咱们继续聊 C# 语言基础的下半部分:流程控制、数组和字符串操作。