1. 为什么C#基础语法如此重要?
作为一名从VB6时代就开始使用微软技术栈的老程序员,我见证了C#从1.0到11.0的完整进化历程。很多初学者常犯的一个错误是,在还没完全掌握基础语法的情况下,就急着去学习WPF、ASP.NET Core这些框架。这就像还没学会走路就想跑马拉松——你可能会勉强完成,但姿势一定很难看。
C#的基础语法是你编程思维的基石。我面试过上百个C#开发者,发现一个规律:那些在基础语法上表现扎实的候选人,即使对某些高级框架不熟悉,也能快速上手;而那些基础薄弱但会"炫技"的,往往在解决实际问题时漏洞百出。
2. C#基础语法全景图
2.1 变量与数据类型
C#是强类型语言,这意味着每个变量都必须声明其类型。这是与JavaScript等脚本语言最大的区别之一。来看几个核心数据类型:
csharp复制// 值类型
int age = 30; // 32位整数
double price = 9.99; // 双精度浮点
decimal salary = 5000.00m; // 金融计算专用
char grade = 'A'; // 单个字符
bool isActive = true; // 布尔值
// 引用类型
string name = "张三"; // 字符串
object obj = new object(); // 所有类型的基类
dynamic dyn = 10; // 动态类型
关键经验:在金融计算中一定要用decimal而不是double,因为double会有精度损失。我在早期项目中就踩过这个坑,导致财务计算结果出现几分钱的差异。
2.2 控制流程
控制语句是程序的骨架,C#提供了完整的控制结构:
csharp复制// if-else
if (score >= 90)
{
Console.WriteLine("优秀");
}
else if (score >= 60)
{
Console.WriteLine("及格");
}
else
{
Console.WriteLine("不及格");
}
// switch (C# 8.0后支持更强大的模式匹配)
string result = score switch
{
>= 90 => "优秀",
>= 60 => "及格",
_ => "不及格"
};
// 循环
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
while (condition)
{
// 代码块
}
do
{
// 至少执行一次
} while (condition);
2.3 方法与参数传递
方法是代码重用的基本单元,理解参数传递方式至关重要:
csharp复制// 值传递
void UpdateValue(int num)
{
num = 10; // 不影响原始值
}
// 引用传递
void UpdateRef(ref int num)
{
num = 10; // 修改原始值
}
// 输出参数
void GetValues(out int x, out int y)
{
x = 5;
y = 10;
}
// 使用
int a = 1, b;
UpdateValue(a); // a仍为1
UpdateRef(ref a); // a变为10
GetValues(out a, out b);
实际项目经验:在性能敏感的场景中,对于大型结构体,使用ref传递可以避免复制开销。我在图形处理项目中通过这种方式提升了15%的性能。
3. 面向对象编程核心
3.1 类与对象
类是C#面向对象的基础,理解字段、属性、方法的区别很重要:
csharp复制public class Person
{
// 字段
private string _name;
// 属性(C# 3.0自动属性)
public int Age { get; set; }
// 完整属性
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException(nameof(value));
}
// 方法
public string GetGreeting() => $"你好,我是{Name},今年{Age}岁";
// 构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
3.2 继承与多态
csharp复制public class Animal
{
public virtual void MakeSound() => Console.WriteLine("...");
}
public class Dog : Animal
{
public override void MakeSound() => Console.WriteLine("汪汪!");
}
// 使用
Animal myDog = new Dog();
myDog.MakeSound(); // 输出"汪汪!" - 多态的体现
设计建议:优先使用组合而非继承。我在一个电商项目中过度使用继承,导致后期难以维护。现在我会先用组合,只有当"is-a"关系非常明确时才考虑继承。
3.3 接口与抽象类
csharp复制public interface ILogger
{
void Log(string message);
}
public abstract class Shape
{
public abstract double Area();
}
public class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine(message);
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area() => Math.PI * Radius * Radius;
}
4. 现代C#语法糖
4.1 空值处理
csharp复制// 空条件运算符
string name = person?.Name;
// 空合并运算符
string displayName = name ?? "无名氏";
// 空值检查
if (name is null) throw new ArgumentNullException(nameof(name));
// C# 8.0可空引用类型
#nullable enable
string notNullString = "Hello";
string? nullableString = null;
4.2 模式匹配
csharp复制// 类型模式
if (obj is string s)
{
Console.WriteLine(s.Length);
}
// 属性模式
if (person is { Age: >= 18 })
{
Console.WriteLine("成年人");
}
// 元组模式
var result = (x, y) switch
{
(0, 0) => "原点",
(_, 0) => "x轴上",
(0, _) => "y轴上",
_ => "其他位置"
};
4.3 记录类型(C# 9)
csharp复制public record Point(int X, int Y);
// 使用
var p1 = new Point(1, 2);
var p2 = p1 with { Y = 3 }; // 不可变对象的"修改"
5. 异常处理最佳实践
csharp复制try
{
// 可能抛出异常的代码
}
catch (FileNotFoundException ex)
{
// 特定异常处理
Logger.Error($"文件未找到: {ex.FileName}");
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
{
// 带条件的异常捕获
Logger.Error($"文件访问错误: {ex.Message}");
}
finally
{
// 清理资源
}
血泪教训:永远不要捕获所有异常而不处理!我在维护一个遗留系统时发现到处都是空的catch块,导致问题被静默忽略,最终酿成大错。
6. 集合与LINQ
6.1 常用集合类型
csharp复制List<string> names = new() { "Alice", "Bob" };
Dictionary<int, string> employees = new()
{
[101] = "张三",
[102] = "李四"
};
// C# 9目标类型推导
HashSet<int> numbers = new() { 1, 2, 3 };
6.2 LINQ查询
csharp复制var results = from p in people
where p.Age > 18
orderby p.Name
select new { p.Name, p.Age };
// 方法语法
var adults = people.Where(p => p.Age > 18)
.OrderBy(p => p.Name)
.Select(p => new { p.Name, p.Age });
7. 委托与事件
csharp复制public delegate void NotifyHandler(string message);
public class Publisher
{
public event NotifyHandler? Notify;
public void DoWork()
{
// 工作...
Notify?.Invoke("工作完成");
}
}
// 使用
var pub = new Publisher();
pub.Notify += msg => Console.WriteLine($"收到通知: {msg}");
pub.DoWork();
性能提示:事件处理要快速返回,避免长时间运行。我在一个UI项目中因为事件处理太慢导致界面卡顿,后来改用异步事件处理解决了问题。
8. 异步编程模式
csharp复制public async Task<string> GetDataAsync()
{
using var client = new HttpClient();
return await client.GetStringAsync("https://example.com");
}
// 使用
try
{
string data = await GetDataAsync();
Console.WriteLine(data);
}
catch (HttpRequestException ex)
{
Console.WriteLine($"请求失败: {ex.Message}");
}
9. 资源管理与using语句
csharp复制// 传统方式
FileStream? file = null;
try
{
file = new FileStream("test.txt", FileMode.Open);
// 使用文件流
}
finally
{
file?.Dispose();
}
// 现代方式
using var file = new FileStream("test.txt", FileMode.Open);
// 自动释放
10. 调试技巧与常见陷阱
10.1 调试技巧
- 条件断点:右键断点→条件
- 即时窗口:调试时输入表达式查看值
- 数据提示:悬停变量查看值
- 调用堆栈:追踪方法调用链
10.2 常见陷阱
csharp复制// 陷阱1:浮点数相等比较
double a = 0.1 + 0.2;
if (a == 0.3) // false!
{
// 不会执行
}
// 正确方式
if (Math.Abs(a - 0.3) < 0.0001) {}
// 陷阱2:字符串不变性
string s = "hello";
s.ToUpper(); // 不会改变s
Console.WriteLine(s); // 输出"hello"
// 正确方式
s = s.ToUpper();
// 陷阱3:装箱拆箱
int i = 123;
object o = i; // 装箱
int j = (int)o; // 拆箱
11. 编码规范与最佳实践
-
命名约定:
- 类名:PascalCase
- 方法名:PascalCase
- 局部变量:camelCase
- 私有字段:_camelCase
-
注释规范:
- 使用XML注释公开API
- 避免无意义的注释
- 用注释解释"为什么"而不是"做什么"
-
代码组织:
- 一个文件一个类
- 方法不超过20行
- 类不超过300行
12. 下一步学习路径
掌握了基础语法后,建议按以下顺序深入学习:
- .NET核心概念:CLR、GC、程序集
- 常用类库:System.IO、System.Collections.Generic
- 高级特性:反射、特性、表达式树
- 框架学习:ASP.NET Core、WPF、Entity Framework
- 设计模式:特别是Repository、Strategy、Observer
记住:基础语法就像乐高积木的基本块,掌握了它们,你就能构建任何复杂的结构。我在职业生涯中见过太多开发者因为基础不牢而遇到瓶颈,希望你能避免这个陷阱。
