C#语言基础详解
一、C#语言概述
C#是一种现代化、创新、开放源代码、跨平台 的面向对象编程语言,是GitHub上排名前列的5种编程语言之一。它具有以下特点:
- 类型安全 :编译时检查数据类型,减少运行时错误
- 面向对象 :支持类、继承、多态等OOP特性
- 泛型 :提供类型安全的集合和算法
- 模式匹配 :简化条件判断
- 异步编程 :通过async/await简化异步代码
- 记录类型 :简化数据模型定义
C#是.NET平台的核心开发语言,可用于开发Windows桌面应用、Web应用、移动应用、游戏、云服务等多种类型的应用。
二、数据类型
C#将数据类型分为值类型 和引用类型 :
1. 值类型(存储在栈上)
表格
|-----------|----------------------------------------------|-------------------------------|
| 类型 | 说明 | 示例 |
| 整数类型 | 用于存储整数值 | int age = 25; |
| byte | 1字节,0-255 | byte count = 100; |
| short | 2字节,-32768到32767 | short score = 85; |
| int | 4字节,-2147483648到2147483647 | int age = 30; |
| long | 8字节,-9223372036854775808到9223372036854775807 | long population = 7800000000; |
| 浮点数类型 | 用于存储小数值 | |
| float | 单精度,约7位有效数字(需在数值后加f/F) | float pi = 3.14f; |
| double | 双精度,约15-16位有效数字 | double pi = 3.1415926535; |
| 字符类型 | 用于存储单个Unicode字符 | char grade = 'A'; |
| 布尔类型 | 只能是true或false | bool isStudent = true; |
2. 引用类型(存储在堆上,指向内存地址)
表格
|---------|-------------|---------------------------------|
| 类型 | 说明 | 示例 |
| 字符串 | 表示字符序列,不可变 | string name = "Alice"; |
| 数组 | 存储多个相同类型的数据 | int[] numbers = new int[5]; |
| 类 | 定义对象的蓝图 | class Person { ... } |
三、变量与常量
1. 变量声明与初始化****csharp
cs
// 显式类型声明
int age = 25; // 值类型:整数
string name = "Alice"; // 引用类型:字符串
bool isActive = true; // 值类型:布尔
// 隐式类型声明(var)
var count = 10; // 编译器推断为int
var price = 9.99; // 编译器推断为double
var message = "Hello"; // 编译器推断为string
// 多变量声明
int a, b, c; // 声明三个整型变量
a = 10; b = 20; c = 30;
2. 常量****csharp
cs
const double PI = 3.14159; // 常量,必须在声明时初始化
const int MAX_USERS = 100;
四、运算符
1. 算术运算符****csharp
cs
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
2. 关系运算符****csharp
cs
bool isEqual = (a == b); // false
bool isNotEqual = (a != b); // true
bool isGreater = (a > b); // true
bool isLess = (a < b); // false
bool isGreaterOrEqual = (a >= b); // true
bool isLessOrEqual = (a <= b); // false
3. 逻辑运算符****csharp
cs
bool isAdult = (age >= 18); // true
bool isMale = true;
bool isStudent = false;
bool isAdultMale = isAdult && isMale; // true
bool isAdultOrStudent = isAdult || isStudent; // true
bool isNotAdult = !isAdult; // false
五、流程控制语句
1. 条件语句****csharp
cs
// if-else 语句
int score = 85;
if (score >= 90)
{
Console.WriteLine("A");
}
else if (score >= 80)
{
Console.WriteLine("B");
}
else
{
Console.WriteLine("C");
}
// switch 语句
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
default:
Console.WriteLine("Invalid day");
break;
}
2. 循环语句****csharp
cs
// for 循环
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"第{i + 1}次循环");
}
// while 循环
int counter = 0;
while (counter < 5)
{
Console.WriteLine($"当前计数: {counter}");
counter++;
}
// do-while 循环
int num = 0;
do
{
Console.WriteLine($"当前数字: {num}");
num++;
} while (num < 5);
六、字符串操作
cs
// 字符串连接
string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName; // "John Doe"
// 字符串内插(C# 6.0+)
Console.WriteLine($"Hello, {firstName}!"); // "Hello, John!"
// 字符串方法
string text = " Hello, World! ";
Console.WriteLine(text.Length); // 15
Console.WriteLine(text.Trim()); // "Hello, World!"
Console.WriteLine(text.ToUpper()); // " HELLO, WORLD! "
Console.WriteLine(text.ToLower()); // " hello, world! "
Console.WriteLine(text.StartsWith("H")); // true
Console.WriteLine(text.Contains("World")); // true
七、基础语法示例:Hello World****csharp
cs
using System; // 引入System命名空间,包含Console等基础类
namespace HelloWorldApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!"); // 输出"Hello, World!"
Console.ReadKey(); // 保持控制台窗口打开
}
}
}
八、C#语言的演变
C#自2000年发布以来,不断引入新特性,逐步发展为一门强大且现代化的编程语言:
- C# 2.0 :匿名方法、泛型
- C# 3.0 :LINQ、Lambda表达式、自动属性
- C# 4.0 :动态绑定、命名参数
- C# 5.0 :异步编程(async/await)
- C# 6.0 :字符串内插、nameof运算符
- C# 7.0 :元组、模式匹配、局部函数
- C# 8.0 :可空引用类型、异步流
- C# 9.0 :记录类型、模式匹配增强
- C# 10.0 :记录结构、文件范围命名空间
- C# 11.0 :原生支持、字符串插值表达式
- C# 12.0 :模式匹配增强、泛型参数约束
C#面向对象编程核心概念与高级特性详解
一、面向对象编程核心概念
1. 类与对象
类是面向对象编程的基本单元,是创建对象的蓝图。它定义了对象的属性(数据)和行为(方法)。
cs
// 类定义:定义了Person对象的蓝图
public class Person
{
// 属性(字段)
private string _name;
private int _age;
// 构造函数
public Person(string name, int age)
{
_name = name;
_age = age;
}
// 方法
public void Introduce()
{
Console.WriteLine($"Hello, my name is {_name} and I am {_age} years old.");
}
}
对象 是类的实例,通过new关键字创建:
cs
// 创建对象实例
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Bob", 30);
// 调用对象方法
person1.Introduce(); // 输出: Hello, my name is Alice and I am 25 years old.
person2.Introduce(); // 输出: Hello, my name is Bob and I am 30 years old.
2. 封装
封装是将数据(属性)和操作数据的方法(行为)绑定在一起,通过访问修饰符控制对内部状态的访问,保护对象内部实现细节。csha
rp
cs
public class BankAccount
{
private decimal _balance; // 私有字段,外部无法直接访问
// 公共属性,提供安全的访问方式
public decimal Balance
{
get { return _balance; }
set
{
if (value >= 0)
_balance = value;
else
throw new ArgumentException("Balance cannot be negative");
}
}
// 方法
public void Deposit(decimal amount)
{
if (amount > 0)
_balance += amount;
else
throw new ArgumentException("Deposit amount must be positive");
}
}
封装的好处 :
- 保护对象内部状态不被意外修改
- 提供更安全、更可控的接口
- 便于修改内部实现而不影响外部使用
3. 继承
继承允许一个类(派生类)从另一个类(基类)继承属性和方法,实现代码复用和层次结构。csharp
cs
// 基类
public class Vehicle
{
public string Model { get; set; }
public int Year { get; set; }
public virtual void StartEngine()
{
Console.WriteLine("Engine started.");
}
}
// 派生类
public class Car : Vehicle
{
public string Color { get; set; }
// 重写基类方法
public override void StartEngine()
{
Console.WriteLine("Car engine started with a vroom!");
}
}
// 继承的使用
Car myCar = new Car { Model = "Tesla Model S", Year = 2023, Color = "Red" };
myCar.StartEngine(); // 输出: Car engine started with a vroom!
4. 多态
多态允许不同类的对象对同一消息做出不同的响应。在C#中,多态主要通过虚方法(virtual)和重写(override)实现。ar
cs
// 基类
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal speaks");
}
}
// 派生类
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Meow!");
}
}
// 多态示例
Animal myPet = new Dog();
myPet.Speak(); // 输出: Woof!
myPet = new Cat();
myPet.Speak(); // 输出: Meow!
多态的实现方式 :
- 虚方法(virtual)和重写(override)
- 接口实现
- 抽象类
5. 抽象
抽象是面向对象编程的核心,通过抽象类和接口实现。
抽象类 :csharp
cs
public abstract class Shape
{
public abstract double Area(); // 抽象方法,没有实现
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area()
{
return Math.PI * Radius * Radius;
}
}
接口 :
cs
public interface IShape
{
double Area();
}
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double Area()
{
return Width * Height;
}
}
cshrp
抽象的好处 :
- 定义通用契约,使代码更灵活
- 促进代码重用和模块化
- 降低系统耦合度
二、核心特性详解
1. 异常处理(try-catch)
C#的异常处理机制帮助处理程序运行时发生的意外情况,通过try、catch和finally关键字实现。csharp
cs
public class Calculator
{
public int Divide(int a, int b)
{
try
{
return a / b;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Error: Cannot divide by zero.");
// 可以选择重新抛出异常
throw; // 或者 return 0;
}
finally
{
Console.WriteLine("Division operation completed.");
}
}
}
// 使用异常处理
Calculator calc = new Calculator();
try
{
int result = calc.Divide(10, 0);
Console.WriteLine($"Result: {result}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
异常处理的关键点 :
- 所有异常都继承自System.Exception
- try块包含可能引发异常的代码
- catch块捕获并处理特定类型的异常
- finally块确保资源清理,无论是否发生异常
2. LINQ(语言集成查询)
LINQ是C#中将查询功能直接集成到语言的一组技术,允许使用SQL风格语法查询各种数据源。
LINQ查询表达式 :csharp
cs
// 创建数据源
List<int> scores = new List<int> { 97, 92, 81, 60, 55 };
// 查询表达式
var highScores = from score in scores
where score > 80
orderby score descending
select score;
// 执行查询
Console.WriteLine("High scores:");
foreach (var score in highScores)
{
Console.WriteLine(score);
}
// 输出: 97, 92, 81
LINQ方法语法 :csharp
cs
// 使用方法语法的等效查询
var highScores2 = scores.Where(score => score > 80)
.OrderByDescending(score => score);
LINQ的优势 :
- 编译时类型检查
- IntelliSense支持
- 统一查询语法,适用于多种数据源(内存集合、数据库、XML等)
- 查询表达式与方法语法可互换
三、进阶概念详解
1. 委托与Lambda表达式
委托 是类型安全的函数指针,可以引用方法。csharp
cs
// 定义委托类型
public delegate void MessageHandler(string message);
public class MessagePublisher
{
public event MessageHandler OnMessage;
public void PublishMessage(string message)
{
OnMessage?.Invoke(message); // 调用所有订阅者
}
}
public class MessageSubscriber
{
public void HandleMessage(string message)
{
Console.WriteLine($"Received: {message}");
}
}
// 使用委托
MessagePublisher publisher = new MessagePublisher();
MessageSubscriber subscriber = new MessageSubscriber();
// 订阅事件
publisher.OnMessage += subscriber.HandleMessage;
// 发布消息
publisher.PublishMessage("Hello, World!"); // 输出: Received: Hello, World!
Lambda表达式 简化了委托的使用:csharp
cs
// 使用Lambda表达式订阅
publisher.OnMessage += (message) => Console.WriteLine($"Lambda received: {message}");
// 简写形式
publisher.OnMessage += message => Console.WriteLine($"Lambda received: {message}");
// 无参数的Lambda
publisher.OnMessage += _ => Console.WriteLine("Message received!");
Lambda表达式的优势 :
- 语法简洁
- 闭包能力(可以捕获外部变量)
- 与LINQ深度集成
2. 异步编程(async/await)
异步编程通过async/await关键字简化了I/O密集型任务的处理,避免阻塞主线程。csharp
cs
public class AsyncExample
{
// 同步方法
public string GetData()
{
// 模拟耗时操作
Thread.Sleep(2000);
return "Data from synchronous method";
}
// 异步方法
public async Task<string> GetDataAsync()
{
// 模拟耗时操作
await Task.Delay(2000);
return "Data from asynchronous method";
}
public async Task ProcessDataAsync()
{
Console.WriteLine("Starting async operation...");
string result = await GetDataAsync();
Console.WriteLine($"Async operation completed. Result: {result}");
}
}
// 使用异步方法
var example = new AsyncExample();
var task = example.ProcessDataAsync();
Console.WriteLine("Doing other work while waiting for async operation...");
// 等待任务完成
await task;
Console.WriteLine("Async operation completed.");
异步编程的关键点 :
- async关键字标记异步方法
- await关键字等待异步操作完成
- 异步方法返回Task或Task
- 避免阻塞主线程,提高应用程序响应性
异步Linq示例 :csharp
cs
// 使用异步Linq查询
public async Task<List<string>> GetUsersAsync()
{
using (var context = new AppDbContext())
{
// 异步查询
return await context.Users
.Where(u => u.IsActive)
.Select(u => u.Name)
.ToListAsync();
}
}
四、面向对象编程最佳实践
- 遵循SOLID原则 :
- 单一职责原则(SRP)
- 开闭原则(OCP)
- 里氏替换原则(LSP)
- 接口隔离原则(ISP)
- 依赖倒置原则(DIP)
- 设计模式应用 :
- 工厂模式:创建对象而不指定具体类
- 单例模式:确保一个类只有一个实例
- 观察者模式:定义对象间的一对多依赖关系
- 策略模式:定义一系列算法,使它们可以互相替换
- 代码组织 :
- 保持类小而专注
- 使用合适的访问修饰符
- 避免过度继承,优先使用组合
- 通过接口定义契约
五、总结
C#的面向对象编程特性是构建健壮、可维护、可扩展应用程序的基础。通过掌握类、对象、继承、多态、封装和抽象这些核心概念,以及熟练运用异常处理、LINQ、委托/Lambda和异步编程等高级特性,你将能够:
- 编写结构清晰、易于维护的代码
- 实现高效的错误处理机制
- 简化复杂的数据查询
- 创建高性能、响应迅速的应用程序
- 设计灵活、可扩展的系统架构
这些特性共同构成了C#作为现代编程语言的强大基础,使它成为.NET平台开发的首选语言。随着C#的持续演进(如C# 12+引入的记录类型、模式匹配等新特性),面向对象编程在C#中的应用将更加丰富和高效。
异步LINQ查询详解:GetUsersAsync方法
这个方法是C#中使用Entity Framework Core进行异步数据库查询的典型示例,结合了LINQ查询和异步编程的最佳实践。下面我将从多个角度详细解析这个方法:
一、方法结构解析
cs
public async Task<List<string>> GetUsersAsync()
{
using (var context = new AppDbContext())
{
return await context.Users
.Where(u => u.IsActive)
.Select(u => u.Name)
.ToListAsync();
}
}
- 异步方法签名
cs
public async Task<List<string>> GetUsersAsync()
- async关键字:表示这是一个异步方法,内部可以使用await表达式
- Task<List<string>>:表示该方法返回一个异步操作,当完成时将提供一个List结果
- 重要原则 :遵循"异步一路"(Async All the Way)原则,确保调用链都是异步的
首要原则是"异步一路":一旦在调用栈的某个层次使用了异步,则其上层调用也应尽可能异步化,以避免死锁或线程池耗尽。
2. 资源管理:using语句****csharp
cs
using (var context = new AppDbContext())
- 确保AppDbContext实例在使用后被正确释放
- DbContext是Entity Framework Core的上下文,用于与数据库交互
- using确保在代码块结束后,context对象会被自动释放,避免资源泄漏
3. 异步LINQ查询****csharp
cs
public async Task<List<string>> GetUsersAsync()
这是该方法的核心,让我们分步解析:
二、LINQ查询的延迟执行特性
关键点 :LINQ查询默认采用延迟执行 (Deferred Execution),这意味着:
- 从context.Users到.Select的整个链式调用不会立即执行 SQL查询
- 只有在调用.ToListAsync()(或类似立即执行方法)时,才会真正执行数据库查询
- LINQ查询表达式默认采用延迟执行,即只有在迭代结果时才会真正运行查询。
1.****.Where(u => u.IsActive)
- 筛选条件:只获取IsActive属性为true的用户
- 这是LINQ的Where方法,通过Lambda表达式定义条件
- 重要 :这个条件将被转换为SQL的WHERE子句,在数据库层面执行,而不是在内存中
2.****.Select(u => u.Name)
- 选择操作:只获取用户的Name字段,而不是整个用户对象
- 这是LINQ的Select方法,通过Lambda表达式定义投影
- 性能优势 :减少了网络传输数据量和内存占用
Select负责映射,相当于SQL的SELECT部分。错误示范:Select不能过滤数据,正确写法先Where再Select。
3.****.ToListAsync()
- 异步版本的ToList,将查询结果转换为列表
- 与同步的ToList()不同,ToListAsync()是异步的,不会阻塞主线程
- 会触发真正的数据库查询执行
*对于Entity Framework Core等ORM,使用ToListAsync(),**FirstOrDefaultAsync()*等异步方法可以避免阻塞线程,提高应用程序的响应能力和吞吐量。
三、为什么使用异步查询
1. I/O密集型操作的优化
- 数据库查询是典型的I/O密集型操作
- 同步查询会阻塞调用线程,直到数据库响应
- 异步查询在等待数据库响应时,会释放线程,让线程池处理其他请求
如果代码实现 I/O 绑定方案以支持网络数据请求、数据库访问或文件系统读取/写入,则异步编程是最佳方法。
2. 与传统同步查询的对比****csharp
cs
// 同步查询(不推荐,会阻塞线程)
public List<string> GetUsersSync()
{
using (var context = new AppDbContext())
{
return context.Users
.Where(u => u.IsActive)
.Select(u => u.Name)
.ToList();
}
}
cs
// 异步查询(推荐)
public async Task<List<string>> GetUsersAsync()
{
using (var context = new AppDbContext())
{
return await context.Users
.Where(u => u.IsActive)
.Select(u => u.Name)
.ToListAsync();
}
}
以前用Thread,现在用async/await,异步编程才变得优雅。
四、性能与最佳实践
1. 为什么先Where后Selectcsharp
cs
// 正确写法
context.Users
.Where(u => u.IsActive)
.Select(u => u.Name)
cs
// 错误写法
context.Users
.Select(u => u.Name)
.Where(u => u.IsActive) // 这会导致在内存中过滤,而不是在数据库
- 先过滤再投影:数据库先筛选数据,然后只返回需要的字段
- 先投影再过滤:数据库返回所有字段,然后在内存中过滤,效率低下
正确写法先Where再Select。
2. 避免重复执行****csharp
cs
// 定义一个查询(此时不会访问数据库)
var query = context.Users.Where(u => u.IsActive).Select(u => u.Name);
// 第一次迭代:执行查询,访问数据库
foreach (var name in query) { ... }
// 第二次迭代:再次执行查询,再次访问数据库!
foreach (var name in query) { ... }
- 如果需要多次使用查询结果,应该使用ToList()或ToArray()立即执行
从*LINQ查询表达式默认采用延迟执行...使用ToList(),ToArray(),**Count()*等方法会立即触发查询执行。
五、与知识库信息的关联
- 异步编程模型 :
- C#具有语言级异步编程模型,可让你轻松编写异步代码,而无需杂乱回调或符合支持异步的库。
- 当一个方法被标记为async时,它预示着该方法内部可能包含await表达式。
- LINQ查询特性 :
- 标准查询运算符作为扩展方法来实现。
- 查询的类型为IEnumerable。所有这些查询都可以使用var编写。
- 异步Linq的优势 :
- 从Linq与异步编程的结合为复杂数据处理提供了声明式与高性能并存的解决方案。
- 通过IAsyncEnumerable接口的桥梁作用,开发者可以像操作传统集合那样使用Where、Select等方法处理异步数据流。
六、实际应用场景
这个方法在Web API或服务层非常常见,例如:
cs
[HttpGet("active-users")]
public async Task<ActionResult<List<string>>> GetActiveUsers()
{
var users = await _userService.GetUsersAsync();
return Ok(users);
}
- 在ASP.NET Core中,这个异步方法可以避免阻塞请求线程
- 使服务器能同时处理更多并发请求
- 提高应用程序的整体吞吐量和响应能力
七、总结
这个GetUsersAsync方法展示了C#中异步LINQ查询的最佳实践:
- 正确使用异步 :通过async/await避免阻塞主线程
- 正确使用LINQ :先筛选后投影,减少数据传输
- 正确管理资源 :使用using确保DbContext正确释放
- 遵循最佳实践 :遵循"异步一路"原则,避免死锁和线程池耗尽
通过这种写法,我们可以编写出既高效又可维护的代码,特别适用于需要处理大量数据的Web应用和API服务。