C#教程(2024最新):基础、进阶与实战

C# 教程 (2024 最新):基础、进阶与实战

C# (读作 "C Sharp") 是由微软开发的一种现代、通用、面向对象的编程语言。它以其强大的功能、类型安全、以及与 .NET 生态系统的紧密集成而闻名。无论你是编程新手还是希望扩展技能的经验丰富的开发者,本教程都将带你从基础到进阶,最终通过实战项目掌握 C# 编程。

一、基础篇:C# 入门

1.1 开发环境搭建

  • Visual Studio (推荐): 微软官方提供的集成开发环境 (IDE),提供代码编辑、调试、编译、项目管理等全方位支持。下载 Visual Studio Community (免费版) 即可满足学习需求。 安装时,务必勾选 ".NET desktop development" 工作负载。
  • Visual Studio Code: 轻量级、跨平台的代码编辑器,配合 C# 扩展 (OmniSharp) 也能提供良好的开发体验。需要单独安装 .NET SDK。
  • .NET SDK: 包含 C# 编译器、运行时 (CLR) 和基础类库。 可以从微软官网下载。

验证安装: 打开命令行 (Windows) 或终端 (macOS/Linux),输入 dotnet --version,如果显示 .NET SDK 版本号,则表示安装成功。

1.2 第一个 C# 程序: "Hello, World!"

```csharp
using System;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
```

  • using System;: 导入 System 命名空间,其中包含常用的类 (如 Console)。
  • namespace HelloWorld: 定义一个命名空间,用于组织代码,避免命名冲突。
  • class Program: 定义一个名为 Program 的类。 C# 程序通常从一个包含 Main 方法的类开始执行。
  • static void Main(string[] args): 程序的入口点。static 表示该方法属于类本身,而不是类的实例。void 表示该方法不返回值。string[] args 用于接收命令行参数 (可选)。
  • Console.WriteLine("Hello, World!");: 调用 Console 类的 WriteLine 方法,在控制台输出文本。

编译和运行:

  1. 使用 Visual Studio: 创建新项目 (Console App),粘贴代码,点击 "Start" 或按 F5 键。
  2. 使用 .NET CLI: 将代码保存为 Program.cs 文件, 打开命令行,导航到文件所在目录,执行以下命令:
    bash
    dotnet build # 编译
    dotnet run # 运行

1.3 C# 基础语法

  • 变量和数据类型:
    • 基本数据类型: int (整数), float (单精度浮点数), double (双精度浮点数), bool (布尔值, truefalse), char (字符), string (字符串)。
    • 变量声明: 数据类型 变量名 = 值; (例如: int age = 30;)
    • 类型推断 (var): var name = "Alice"; (编译器根据初始值自动推断类型)。
  • 运算符:
    • 算术运算符: +, -, *, /, % (取模)。
    • 关系运算符: == (等于), != (不等于), >, <, >=, <=.
    • 逻辑运算符: && (与), || (或), ! (非)。
    • 赋值运算符: =, +=, -=, *=, /=, %=
    • 条件运算符 (三元运算符): 条件 ? 表达式1 : 表达式2 (如果条件为真,返回表达式1的值,否则返回表达式2的值)。
  • 控制流:
    • if-else 语句: 根据条件执行不同的代码块。
      csharp
      if (age >= 18)
      {
      Console.WriteLine("成年");
      }
      else
      {
      Console.WriteLine("未成年");
      }
    • switch 语句: 根据表达式的值执行不同的代码块。
      csharp
      switch (dayOfWeek)
      {
      case 1:
      Console.WriteLine("星期一");
      break;
      case 2:
      Console.WriteLine("星期二");
      break;
      // ... 其他 case
      default:
      Console.WriteLine("无效的星期");
      break;
      }
    • for 循环: 重复执行一段代码,指定循环次数。
      csharp
      for (int i = 0; i < 10; i++)
      {
      Console.WriteLine(i);
      }
    • while 循环: 当条件为真时,重复执行一段代码。
      csharp
      int count = 0;
      while (count < 5)
      {
      Console.WriteLine(count);
      count++;
      }
    • do-while 循环: 至少执行一次代码块,然后当条件为真时重复执行。
      csharp
      int num = 1;
      do
      {
      Console.WriteLine(num);
      num++;
      } while (num <= 3);
    • breakcontinue 语句: break 用于跳出循环,continue 用于跳过当前循环迭代,继续下一次迭代。
  • 注释:
    • 单行注释:// 这是单行注释
    • 多行注释:/* 这是多行注释 */
    • 文档注释:/// <summary>这是方法的文档注释</summary> (用于生成 API 文档)

1.4 数组、集合和字符串

  • 数组 (Array): 存储固定大小的相同类型元素的集合。
    csharp
    int[] numbers = new int[5]; // 创建一个包含 5 个整数的数组
    numbers[0] = 10; // 访问和修改数组元素
    int firstNumber = numbers[0];
  • 集合 (Collections): 提供更灵活的数据存储方式 (动态大小、不同类型元素等)。
    • List<T> (列表): 有序、可变大小的集合。
      csharp
      List<string> names = new List<string>();
      names.Add("Bob");
      names.Add("Charlie");
      string secondName = names[1];
    • Dictionary<TKey, TValue> (字典): 键值对集合,通过键快速查找值。
      csharp
      Dictionary<string, int> ages = new Dictionary<string, int>();
      ages["Alice"] = 25;
      ages["David"] = 35;
      int aliceAge = ages["Alice"];
    • HashSet<T> (集合): 无序、不重复元素的集合。
    • Queue<T> (队列): 先进先出 (FIFO) 集合。
    • Stack<T> (栈): 后进先出 (LIFO) 集合。
  • 字符串 (String):
    • 字符串插值: $"My name is {name} and I am {age} years old." (更简洁的字符串拼接方式)。
    • 常用字符串方法: Length (长度), Substring (截取子串), IndexOf (查找子串), Replace (替换), ToUpper (转大写), ToLower (转小写), Trim (去除首尾空白字符)。
    • StringBuilder: 对于频繁修改的字符串,使用 StringBuilder 类可以提高性能,因为它避免了创建多个字符串对象的开销。

1.5 枚举 (Enums)

  • 枚举是一种值类型,用于定义一组命名的整数常量。
    ```csharp
    enum DaysOfWeek
    {
    Monday, // 默认值为 0
    Tuesday, // 1
    Wednesday, // 2
    Thursday, // 3
    Friday, // 4
    Saturday, // 5
    Sunday // 6
    }

    DaysOfWeek today = DaysOfWeek.Wednesday;

    // 可以将枚举值转换为整数,反之亦然
    int dayValue = (int)today; // dayValue = 2
    DaysOfWeek tomorrow = (DaysOfWeek)3; // tomorrow = DaysOfWeek.Thursday

    //可以自定义枚举值
    enum ErrorCodes
    {
    None = 0,
    NotFound = 404,
    BadRequest = 400,
    Unauthorized = 401
    }
    ```

二、进阶篇:面向对象编程 (OOP)

2.1 类和对象

  • 类 (Class): 对象的蓝图或模板,定义了对象的属性 (数据) 和方法 (行为)。
    ```csharp
    class Dog
    {
    // 属性 (字段)
    public string Name;
    public string Breed;
    public int Age;

    // 方法
    public void Bark()
    {
        Console.WriteLine("Woof!");
    }
    
    public void Eat(string food)
    {
        Console.WriteLine($"{Name} is eating {food}.");
    }
    

    }
    * **对象 (Object):** 类的实例,具有类定义的属性和方法。csharp
    Dog myDog = new Dog(); // 创建 Dog 类的实例
    myDog.Name = "Buddy";
    myDog.Breed = "Golden Retriever";
    myDog.Age = 3;
    myDog.Bark(); // 调用方法
    myDog.Eat("kibble");
    ```

2.2 构造函数

  • 构造函数 (Constructor): 用于初始化对象的特殊方法,与类同名,没有返回值类型。
    ```csharp
    class Cat
    {
    public string Name;
    public string Color;

    // 构造函数
    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
      //无参数构造函数
       public Cat()
       {
          Name = "unnamed";
          Color = "unknown"
       }
    // 可以有多个构造函数 (重载)
    public Cat(string name) : this(name, "Unknown") { }  //调用上面的双参数构造函数.
    

    }

    Cat myCat = new Cat("Whiskers", "Gray"); // 使用构造函数创建对象
    Cat anotherCat = new Cat("Tom"); //使用重载的单参数构造函数
    Cat namelessCat = new Cat(); //使用无参数构造函数
    ```

2.3 封装 (Encapsulation)

  • 访问修饰符: public (公开, 任何地方都可以访问), private (私有, 只能在类内部访问), protected (受保护, 只能在类内部和派生类中访问), internal (内部, 只能在同一程序集中访问)。
  • 属性 (Properties): 提供对类字段的受控访问。 属性通常包含 get (读取) 和 set (写入) 访问器。
    ```csharp
    class Person
    {
    private string _name; // 私有字段

    public string Name  // 公开属性
    {
        get { return _name; }
        set
        {
            if (!string.IsNullOrEmpty(value))
            {
                _name = value;
            }
        }
    }
    
    // 自动实现的属性 (简写形式)
    public int Age { get; set; }
    //只读属性
    public string FullName {get;} = "John Doe";
    

    }
    ```

2.4 继承 (Inheritance)

  • 继承: 允许一个类 (子类/派生类) 从另一个类 (父类/基类) 继承属性和方法,实现代码重用和层次结构。
    ```csharp
    // 基类 (父类)
    class Animal
    {
    public string Name { get; set; }

    public virtual void MakeSound() // 虚方法, 允许子类重写
    {
        Console.WriteLine("Generic animal sound");
    }
    

    }

    // 派生类 (子类)
    class Dog : Animal
    {
    public string Breed { get; set; }

    // 重写 (override) 父类的方法
    public override void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
    

    }
    //派生类 (子类)
    class Cat : Animal
    {
    public string Color {get; set;}
    //重写 (override) 父类方法
    public override void MakeSound()
    {
    Console.WriteLine("Meow!");
    }
    }
    ```

2.5 多态 (Polymorphism)

  • 多态: 允许使用父类类型的变量引用子类对象,并在运行时调用子类的方法 (动态绑定)。
    ```csharp
    Animal myAnimal = new Dog(); // 父类变量引用子类对象
    myAnimal.MakeSound(); // 输出 "Woof!" (调用 Dog 类的 MakeSound 方法)

    myAnimal = new Cat();
    myAnimal.MakeSound(); //输出 "Meow!" (调用 Cat 类的 MakeSound方法)
    ```

2.6 抽象类和接口

  • 抽象类 (Abstract Class): 不能实例化的类,用于定义派生类必须实现的抽象成员 (抽象方法和抽象属性)。
    ```csharp
    abstract class Shape
    {
    public abstract double Area { get; } // 抽象属性
    public abstract void Draw(); // 抽象方法
    }

    class Circle : Shape
    {
    public double Radius { get; set; }

    public override double Area
    {
        get { return Math.PI * Radius * Radius; }
    }
    
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
    

    }
    * **接口 (Interface):** 定义一组方法、属性、事件和索引器的契约,类可以实现多个接口。csharp
    interface IPrintable
    {
    void Print();
    }

    interface ILoggable
    {
    void Log(string message);
    }

    class Document : IPrintable, ILoggable
    {
    public void Print()
    {
    Console.WriteLine("Printing document...");
    }

    public void Log(string message)
    {
        Console.WriteLine($"Log: {message}");
    }
    

    }
    ```

2.7 泛型 (Generics)

  • 泛型允许编写可重用的代码,处理不同类型的数据,而无需为每种类型编写单独的代码。
    ```csharp
    class MyList // T 是类型参数
    {
    private T[] _items;
    private int _count;

    public MyList(int capacity)
    {
        _items = new T[capacity];
        _count = 0;
    }
    
    public void Add(T item)
    {
        _items[_count] = item;
        _count++;
    }
    
    public T GetItem(int index)
    {
        return _items[index];
    }
    

    }

    MyList intList = new MyList(10);
    intList.Add(5);
    int number = intList.GetItem(0);

    MyList stringList = new MyList(5);
    stringList.Add("Hello");
    string text = stringList.GetItem(0);
    * 常用的泛型集合: `List<T>`, `Dictionary<TKey, TValue>`, `HashSet<T>`, `Queue<T>`, `Stack<T>`.
    * 泛型约束: 可以使用 `where` 子句对类型参数进行约束。
    csharp
    //约束T必须是引用类型
    public void MyMethod(T value) where T : class
    {
    //...
    }
    //约束T必须是值类型
    public void MyMethod2(T value) where T: struct
    {

    }
    //约束T必须实现IComparable接口
    public void MyMethod3(T value) where T : IComparable
    {

    }
    

    ```

2.8 委托 (Delegates) 和事件 (Events)

  • 委托 (Delegate): 类型安全的函数指针,可以引用具有相同签名的方法。
    ```csharp
    delegate void MyDelegate(string message); // 定义委托类型

    class Messenger
    {
    public void SendMessage(string message, MyDelegate callback)
    {
    Console.WriteLine($"Sending message: {message}");
    callback(message); // 调用委托
    }
    }

    class Logger
    {
    public static void LogMessage(string message)
    {
    Console.WriteLine($"Log: {message}");
    }
    }

    // 使用委托
    Messenger messenger = new Messenger();
    messenger.SendMessage("Hello, world!", Logger.LogMessage); // 传递方法作为委托参数

    //匿名方法
    messenger.SendMessage("Test Anonymous", delegate(string msg){
    Console.WriteLine($"Anonymous method received: {msg}");
    });
    * **事件 (Event):** 基于委托的机制,允许对象在特定事件发生时通知其他对象。csharp
    class Button
    {
    // 定义事件 (基于委托)
    public event EventHandler Click;

    // 触发事件的方法
    protected virtual void OnClick()
    {
        Click?.Invoke(this, EventArgs.Empty); // 使用 ?. 运算符安全地调用事件 (如果事件有订阅者)
    }
    //模拟点击
    public void SimulateClick()
    {
      OnClick();
    }
    

    }

    class Form
    {
    public Form(Button button)
    {
    // 订阅事件
    button.Click += Button_Click; // 使用 += 运算符订阅事件, 使用 -= 运算符取消订阅
    }

    // 事件处理程序 (方法)
    private void Button_Click(object sender, EventArgs e)
    {
        Console.WriteLine("Button clicked!");
    }
    

    }
    //使用
    Button myButton = new Button();
    Form myForm = new Form(myButton);
    myButton.SimulateClick();
    ```

2.9 Lambda 表达式

  • Lambda 表达式是创建匿名方法(没有名称的方法)的简洁方式,通常用于委托和 LINQ 查询。
    ```csharp
    // 使用 lambda 表达式创建委托
    MyDelegate myDelegate = (message) => Console.WriteLine($"Lambda: {message}");
    myDelegate("Hello from lambda");

    // 在 LINQ 中使用 lambda 表达式
    List numbers = new List { 1, 2, 3, 4, 5 };
    var evenNumbers = numbers.Where(n => n % 2 == 0); // 筛选偶数
    foreach (int num in evenNumbers) { Console.WriteLine(num); }
    ``
    * Lambda表达式语法:
    (参数列表) => 表达式或语句块* 如果没有参数,使用空括号()
    * 如果只有一个参数,可以省略括号。
    * 如果表达式或语句块只有一行,可以省略大括号
    {}return` 关键字。

三、实战篇:项目开发

3.1 LINQ (Language Integrated Query)

  • LINQ 是一种强大的查询语言,可以用于查询各种数据源 (集合、数组、数据库、XML 等)。
    ```csharp
    List numbers = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    // 查询表达式语法 (Query Expression Syntax)
    var query = from n in numbers
    where n > 5
    orderby n descending
    select n;

    // 方法语法 (Method Syntax)
    var method = numbers.Where(n => n > 5).OrderByDescending(n => n);

    foreach (int num in query) // 或者 method
    {
    Console.WriteLine(num);
    }
    ``
    * 常用的 LINQ 方法:
    Where(筛选),Select(投影),OrderBy(排序),OrderByDescending(降序排序),GroupBy(分组),Join(连接),FirstOrDefault(获取第一个元素或默认值),ToList(转换为列表),ToArray(转换为数组),Count(计数),Sum(求和),Average(平均值),Min(最小值),Max` (最大值)。

3.2 异步编程 (async/await)

  • 异步编程允许程序在执行耗时操作 (如网络请求、文件 I/O) 时不会阻塞主线程,提高程序的响应性。
    ```csharp
    async Task DownloadWebPageAsync(string url)
    {
    using (HttpClient client = new HttpClient())
    {
    // await 关键字用于等待异步操作完成,并获取结果
    string result = await client.GetStringAsync(url);
    return result;
    }
    }

    // 调用异步方法
    async Task ProcessWebPageAsync()
    {
    string url = "https://www.example.com";
    string content = await DownloadWebPageAsync(url); // await 关键字用于等待异步方法完成
    Console.WriteLine($"Downloaded {content.Length} characters from {url}");
    }
    //在Main函数中调用 (需要将Main函数也标记为async)
    static async Task Main(string[] args)
    {
    await ProcessWebPageAsync();
    Console.ReadKey(); //防止控制台关闭
    }
    ``
    *
    async关键字用于标记异步方法。
    *
    await关键字用于等待异步操作完成,并获取结果。await只能在async方法中使用。
    * 异步方法的返回值通常是
    Task(无返回值) 或Task(有返回值)。
    * 使用
    Task.Run` 方法可以将同步方法转换为异步任务。

3.3 文件 I/O

  • C# 提供了丰富的类用于文件读写操作。
    ```csharp
    using System.IO;

    // 写入文本文件
    string filePath = "myFile.txt";
    string text = "Hello, file I/O!";
    File.WriteAllText(filePath, text);

    // 读取文本文件
    string content = File.ReadAllText(filePath);
    Console.WriteLine(content);

    //逐行读取
    using (StreamReader reader = new StreamReader(filePath))
    {
    string line;
    while ((line = reader.ReadLine()) != null)
    {
    Console.WriteLine(line);
    }
    }
    //逐行写入
    using (StreamWriter writer = new StreamWriter(filePath))
    {
    writer.WriteLine("Line 1");
    writer.WriteLine("Line 2");
    }
    //二进制读写
    using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
    {
    writer.Write(123);
    writer.Write("hello");
    }
    using (BinaryReader reader = new BinaryReader(File.Open(filePath,FileMode.Open)))
    {
    int num = reader.ReadInt32();
    string str = reader.ReadString();
    }

    // 使用 using 语句可以确保文件资源在使用后被正确释放 (即使发生异常)
    ``
    * 常用的文件 I/O 类:
    File(静态方法, 用于处理文件),StreamReader(读取文本文件),StreamWriter(写入文本文件),BinaryReader(读取二进制文件),BinaryWriter(写入二进制文件),Directory(处理目录),FileInfo(文件信息),DirectoryInfo` (目录信息)。

3.4 异常处理 (try-catch-finally)

  • 异常处理用于处理程序运行时可能发生的错误,防止程序崩溃。
    csharp
    try
    {
    // 可能引发异常的代码
    int result = 10 / 0; // 除以零会引发 DivideByZeroException
    }
    catch (DivideByZeroException ex)
    {
    // 捕获特定类型的异常
    Console.WriteLine($"Error: {ex.Message}");
    }
    catch (Exception ex)
    {
    // 捕获其他类型的异常
    Console.WriteLine($"An unexpected error occurred: {ex.Message}");
    }
    finally
    {
    // 无论是否发生异常,都会执行的代码 (用于资源清理等)
    Console.WriteLine("Finally block executed.");
    }
    //抛出异常
    void MyMethod(int value)
    {
    if(value < 0)
    {
    throw new ArgumentOutOfRangeException("value","Value cannot be negative.");
    }
    }

    • try 块包含可能引发异常的代码。
    • catch 块用于捕获和处理特定类型的异常。可以有多个 catch 块。
    • finally 块包含无论是否发生异常,都会执行的代码。
    • throw 关键字用于手动引发异常。
    • Exception 类是所有异常类的基类。

3.5 Entity Framework Core (EF Core) (简要介绍)

  • EF Core 是一个对象关系映射器 (ORM),简化了数据库访问。它允许你使用 C# 对象来操作数据库,而无需编写大量的 SQL 代码。
  • 安装 EF Core NuGet 包:Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.SqlServer (或其他数据库提供程序), Microsoft.EntityFrameworkCore.Tools (用于数据库迁移)。
  • 定义实体类 (Entity Class):
    csharp
    public class Product
    {
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    }
  • 定义 DbContext:
    ```csharp
    public class MyDbContext : DbContext
    {
    public DbSet Products { get; set; } // DbSet 表示数据库中的表

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;"); // 替换为你的数据库连接字符串
    }
    

    }
    * 数据库迁移:
    * `Add-Migration InitialCreate` (创建初始迁移)
    * `Update-Database` (应用迁移到数据库)
    * 使用 LINQ 查询数据库:
    csharp
    using (var context = new MyDbContext())
    {
    // 添加数据
    var newProduct = new Product { Name = "Laptop", Price = 1200 };
    context.Products.Add(newProduct);
    context.SaveChanges(); // 保存更改到数据库

    // 查询数据
    var products = context.Products.Where(p => p.Price > 1000).ToList();
    foreach (var product in products)
    {
    Console.WriteLine($"Id: {product.Id}, Name: {product.Name}, Price: {product.Price}");
    }
    }
    ```

3.6 ASP.NET Core (简要介绍)

  • ASP.NET Core 是一个跨平台、高性能的框架,用于构建 Web 应用程序、API 和微服务。

  • 创建新项目: dotnet new webapi -n MyWebApi (创建 Web API 项目)。

  • 控制器 (Controller):
    C#
    [ApiController]
    [Route("[controller]")]
    public class ProductsController : ControllerBase
    {
    [HttpGet]
    public IEnumerable<Product> Get()
    {
    //从数据库中,或者其他数据源中获取数据。
    return new List<Product>
    {
    new Product { Id = 1, Name = "Product 1", Price = 10 },
    new Product { Id = 2, Name = "Product 2", Price = 20 }
    };
    }
    }

  • 启动项目: dotnet run

  • 访问 API: 使用浏览器或 API 测试工具 (如 Postman) 访问 https://localhost:<port>/products (端口号在 Properties/launchSettings.json 文件中)。

学习资源

总结

本教程提供了 C# 编程的全面指南,从基础语法到面向对象编程,再到实战项目开发。通过学习和实践,你将能够掌握 C# 编程技能,构建各种类型的应用程序。记住,持续学习和实践是掌握任何编程语言的关键。祝你编程愉快!

THE END