C# 新手教程 – wiki基地


C# 新手入门教程:从零开始,掌握现代编程基石

欢迎来到 C# 的世界!如果你是编程的初学者,或者想学习一门强大、多用途且在业界广泛应用的语言,那么 C# 是一个非常棒的选择。本篇文章将为你提供一份全面而详细的 C# 新手入门教程,带你从零开始,逐步掌握这门语言的基础知识和核心概念。

我们将从安装开发环境开始,然后深入探讨变量、数据类型、运算符、控制流、函数、面向对象编程的初步概念,以及如何处理集合和输入输出。读完本文,你将能够编写简单的 C# 程序,并为更高级的学习打下坚实的基础。

文章目录

  1. 为什么选择 C#?
    • C# 的优势
    • C# 的应用领域
  2. 准备工作:安装开发环境
    • 选择 IDE:Visual Studio vs. Visual Studio Code
    • 安装 Visual Studio Community
    • 安装 .NET SDK
    • 创建第一个 C# 项目:Hello, World!
    • 理解第一个程序
  3. C# 基础知识
    • 代码注释
    • 变量 (Variables)
      • 声明、初始化和赋值
      • 变量命名规则
      • var 关键字 (隐式类型)
    • 数据类型 (Data Types)
      • 值类型 (Value Types)
      • 引用类型 (Reference Types)
      • 常用内置数据类型详解 (int, double, bool, char, string)
      • 类型安全
    • 运算符 (Operators)
      • 算术运算符
      • 赋值运算符
      • 比较运算符
      • 逻辑运算符
      • 增量/减量运算符
      • 运算符优先级
  4. 控制流 (Control Flow)
    • 条件语句 (Conditional Statements)
      • if 语句
      • if-else 语句
      • if-else if-else 结构
      • switch 语句
    • 循环语句 (Loop Statements)
      • for 循环
      • while 循环
      • do-while 循环
      • foreach 循环
    • 控制循环流程 (breakcontinue)
  5. 函数/方法 (Functions/Methods)
    • 为什么使用方法
    • 方法的定义和结构
    • void 方法 (无返回值)
    • 带有返回值的方法
    • 方法参数
    • static 关键字 (初步理解)
    • Main 方法:程序的入口
  6. 面向对象编程 (OOP) 初探
    • OOP 基本概念:类 (Class) 和对象 (Object)
    • 定义一个简单的类
    • 创建对象实例
    • 类的成员:字段 (Fields) 和方法 (Methods)
    • 属性 (Properties) (初步介绍)
  7. 数组和集合 (Arrays and Collections)
    • 数组 (Arrays)
      • 声明和初始化数组
      • 访问数组元素
      • 数组的 Length 属性
      • 遍历数组 (forforeach)
    • 列表 (List<T>)
      • 使用 List<T> (using System.Collections.Generic;)
      • 创建和初始化列表
      • 添加、访问和移除元素
      • 列表的 Count 属性
      • 遍历列表
      • 数组与列表的选择
  8. 控制台输入和输出 (Console I/O)
    • 向控制台输出 (Console.Write, Console.WriteLine)
    • 从控制台读取输入 (Console.ReadLine)
    • 字符串与数字之间的转换 (int.Parse, double.Parse, Convert.ToInt32, TryParse)
  9. 错误处理 (Error Handling)
    • try-catch
    • 捕获特定类型的异常
  10. 接下来是什么?
    • 深入学习 OOP
    • 文件操作
    • 理解 .NET 生态系统
    • 探索不同的应用开发领域 (桌面、Web、游戏等)
  11. 总结与鼓励

1. 为什么选择 C#?

C# (读作 “See Sharp”) 是微软开发的一种现代、面向对象的编程语言。它于 2000 年首次发布,作为 .NET 框架的一部分,旨在结合 C++ 的强大功能和 Visual Basic 的易用性。如今,C# 已经发展成为一种成熟且功能丰富的语言,并在全球范围内拥有庞大的开发者社区。

C# 的优势:

  • 现代且面向对象 (Object-Oriented): C# 是一个纯粹的面向对象语言,支持封装、继承、多态等 OOP 核心概念,这有助于构建结构清晰、可维护和可扩展的应用程序。
  • 类型安全 (Type-Safe): C# 强制执行严格的类型规则,这意味着在编译时就能发现许多潜在的错误,从而减少运行时错误,提高程序的健壮性。
  • 强大的生态系统 (.NET): C# 运行在 .NET 平台上,这个平台提供了丰富的类库 (BCL – Base Class Library),几乎涵盖了开发各种应用程序所需的各种功能,如文件操作、网络通信、数据结构、UI 开发等。
  • 跨平台能力: 随着 .NET Core (.NET 的开源跨平台版本) 的发展,C# 不再局限于 Windows 平台。现在你可以在 Windows、macOS 和 Linux 上使用 C# 开发应用程序。
  • 高性能: C# 是一种编译型语言,通过 JIT (Just-In-Time) 编译器将代码编译为高性能的机器码。
  • 社区活跃: C# 拥有庞大且活跃的社区,这意味着你可以轻松找到教程、解决方案和支持。
  • 与微软技术深度集成: 如果你希望在 Windows 生态系统、Azure 云平台或使用微软的其他技术,C# 往往是首选语言。

C# 的应用领域:

C# 的应用范围非常广泛,包括:

  • Windows 桌面应用程序: 使用 WPF 或 Windows Forms。
  • 跨平台桌面/移动应用程序: 使用 .NET MAUI (Multi-platform App UI)。
  • Web 应用程序和服务: 使用 ASP.NET Core (现代、高性能的 Web 框架)。
  • 游戏开发: 使用 Unity 游戏引擎 (全球最流行的游戏引擎之一)。
  • 云服务: 在 Microsoft Azure 上构建各种服务。
  • 物联网 (IoT): 开发物联网设备的应用。
  • 人工智能 (AI) 和机器学习 (ML): 使用 ML.NET 等库。
  • 后端服务和 API。

看到这些,是不是对学习 C# 充满期待了?让我们开始吧!

2. 准备工作:安装开发环境

要编写和运行 C# 代码,你需要安装一些必要的软件。

选择 IDE:Visual Studio vs. Visual Studio Code

  • Visual Studio (VS): 这是微软官方提供的一个功能强大的集成开发环境 (IDE),尤其适用于在 Windows 上进行全面的 .NET 开发。它提供了丰富的工具、调试功能、图形界面设计器等。有 Community (免费供个人和小型团队使用)、Professional 和 Enterprise 版本。
  • Visual Studio Code (VS Code): 这是一个轻量级、跨平台的源代码编辑器,可以通过安装扩展来支持多种语言和框架,包括 C#。它更灵活,适合跨平台开发或更简单的项目。

对于初学者,推荐在 Windows 上安装 Visual Studio Community 版本,因为它提供了一个更全面的开发体验。如果你使用 macOS 或 Linux,或者偏好更轻量级的编辑器,可以选择 Visual Studio Code 搭配 C# 扩展和 .NET SDK。

本教程将主要以 Visual Studio 为例进行讲解,但核心的 C# 语法和概念在 VS Code 中同样适用。

安装 Visual Studio Community

  1. 访问 Visual Studio 官网 (https://visualstudio.microsoft.com/zh-hans/vs/community/)。
  2. 下载 Visual Studio Community 版本安装程序。
  3. 运行安装程序。
  4. 在工作负载 (Workloads) 选择界面,至少选择以下一项或多项:
    • .NET 桌面开发: 如果你想开发 Windows 窗体或 WPF 应用。
    • ASP.NET 和 Web 开发: 如果你想开发 Web 应用。
    • .NET Core 跨平台开发: (这个通常会包含在上述工作负载中,但如果你的主要目标是跨平台控制台或库,可以单独勾选)。
    • 如果你不确定,选择 .NET 桌面开发.NET Core 跨平台开发 是一个不错的起点。
  5. 点击安装。安装过程可能需要一些时间,取决于你的网络速度和选择的工作负载。

安装 .NET SDK

Visual Studio 的安装通常会包含一个版本的 .NET SDK。但有时你可能需要特定版本或单独安装。

  1. 访问 .NET 官网 (https://dotnet.microsoft.com/)。
  2. 下载并安装推荐的最新稳定版本的 .NET SDK。安装过程通常很简单,按照提示进行即可。

安装完成后,你就可以开始创建你的第一个 C# 项目了。

创建第一个 C# 项目:Hello, World!

使用 Visual Studio 创建一个简单的控制台应用程序:

  1. 打开 Visual Studio。
  2. 点击 “创建新项目”。
  3. 在模板搜索框中输入 “控制台”,选择 “控制台应用 (.NET)”。确保选择的是 C# 语言版本。
  4. 点击 “下一步”。
  5. 输入项目名称 (例如: HelloWorldApp) 和项目位置。
  6. 点击 “下一步”。
  7. 选择一个 .NET 版本 (通常选择最新的推荐版本)。
  8. 点击 “创建”。

Visual Studio 会自动为你创建一个包含以下代码的 Program.cs 文件:

csharp
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

注意: 在较新的 .NET 版本 (例如 .NET 6 及以上),默认的控制台应用模板使用了顶级语句 (Top-level statements) 功能,代码会非常简洁。在旧版本或更传统的模板中,你可能会看到这样的结构:

“`csharp
using System; // 引入 System 命名空间

namespace HelloWorldApp // 定义一个命名空间,组织代码
{
class Program // 定义一个类,程序的主要逻辑容器
{
static void Main(string[] args) // Main 方法是程序的入口点
{
// 在控制台输出文本
Console.WriteLine(“Hello, World!”);

        // 在某些旧模板或需要等待用户输入时使用,防止程序立刻关闭
        // Console.ReadKey();
    }
}

}
“`
我们将主要讲解这种更传统的结构,因为它包含了 C# 中重要的命名空间、类和方法概念,对于初学者理解程序结构更有帮助。顶级语句是语法糖,理解了传统结构后,再理解顶级语句会更容易。

为了本教程的连贯性,我们将在后续例子中沿用类似传统结构的风格,或者明确指出使用的是顶级语句。请读者根据自己创建项目时看到的模板结构进行对照。如果你使用的是新模板但想练习传统结构,可以手动修改代码。

理解第一个程序 (传统结构)

让我们分析一下传统结构中的 “Hello, World!” 程序:

  • using System;: 这行代码引入了 System 命名空间。命名空间用于组织和分组相关的代码。System 命名空间包含了 .NET 框架中最基础和常用的类,比如用于控制台输入输出的 Console 类。通过 using 指令,你就可以直接使用 Console.WriteLine 而不是写全名 System.Console.WriteLine
  • namespace HelloWorldApp: 定义了一个名为 HelloWorldApp 的命名空间。通常命名空间与项目名称一致,用来避免不同库中类名冲突。
  • class Program: 在 C# 中,几乎所有的代码都位于类 (Class) 内部。类是面向对象编程的基本构建块。Program 是这个类名,你可以给类取任何合法的名字。
  • static void Main(string[] args): 这是程序的入口点 (Entry Point)。当程序启动时,第一行被执行的代码就在 Main 方法里。
    • static: 表示这个方法属于 Program 类本身,而不是类的某个特定对象实例。对于 Main 方法,这通常是必须的。
    • void: 表示这个方法不返回任何值。
    • Main: 方法的名称,它是 C# 程序入口的约定名称。
    • (string[] args): 这是方法的参数列表。string[] args 表示 Main 方法可以接受一个字符串数组作为参数,这些参数通常是从命令行传递给程序的。对于简单的程序,你暂时可以忽略它。
  • { ... }: 大括号 {} 用于定义代码块,表示一个命名空间、类、方法等的开始和结束。
  • Console.WriteLine("Hello, World!");: 这是实际执行任务的一行代码。
    • Console: 位于 System 命名空间的一个类,提供了与控制台进行交互的功能。
    • .WriteLine(): Console 类的一个方法,用于向控制台输出一行文本,并在末尾添加换行符。括号 () 内是要输出的内容。
    • "Hello, World!": 这是一个字符串字面量,即要输出的具体文本。字符串在 C# 中用双引号 " 包围。
    • ;: 分号表示语句的结束。在 C# 中,大多数语句都必须以分号结束。

要运行这个程序,点击 Visual Studio 工具栏上的绿色三角形 “启动” 按钮,或者按下 F5 键(调试模式)或 Ctrl + F5 键(非调试模式)。你会看到一个控制台窗口弹出,显示 “Hello, World!”。

恭喜!你已经成功创建并运行了第一个 C# 程序。接下来,我们将深入学习 C# 的基础语法。

3. C# 基础知识

代码注释

注释是代码中会被编译器忽略的部分,用于解释代码的功能、目的或任何其他对开发者有用的信息。良好的注释是编写易于理解和维护代码的关键。

C# 支持两种主要的注释方式:

  1. 单行注释: 使用双斜杠 //。从 // 到行尾的所有内容都会被视为注释。
    csharp
    // 这是一行单行注释
    int age = 30; // 声明并初始化一个整数变量

  2. 多行注释: 使用 /* 开始,使用 */ 结束。可以跨越多行。
    “`csharp
    /*

    • 这是一个多行注释的例子。
    • 它可以用来解释一个代码块或者方法的详细信息。
      */
      string message = “Hello!”;
      “`

变量 (Variables)

变量是用于存储数据值的容器。在使用变量之前,你需要先声明它,告诉编译器变量的名称和它将存储的数据类型。

声明、初始化和赋值

  • 声明 (Declaration): 告诉编译器变量的名称和类型。
    csharp
    int age; // 声明一个名为 age 的整数变量
    string name; // 声明一个名为 name 的字符串变量

  • 初始化 (Initialization): 在声明变量时给它赋一个初始值。
    csharp
    int age = 30; // 声明并初始化 age 为 30
    string name = "Alice"; // 声明并初始化 name 为 "Alice"
    bool isStudent = true; // 声明并初始化 isStudent 为 true

  • 赋值 (Assignment): 在变量已经声明后,给它赋一个新的值。
    csharp
    age = 31; // 将 age 的值更改为 31
    name = "Bob"; // 将 name 的值更改为 "Bob"

    你可以在声明后、赋值前使用变量,但如果你试图使用一个未被赋值的局部变量,编译器会报错。

变量命名规则

  • 变量名必须以字母或下划线 _ 开头。
  • 变量名可以包含字母、数字和下划线。
  • 变量名不能包含空格或其他特殊字符(如 !, @, #, $, % 等)。
  • 变量名不能是 C# 的关键字(如 int, class, public, void 等)。
  • C# 变量名是区分大小写的 (ageAge 是不同的变量)。
  • 变量名应该具有描述性,以便代码易于理解(例如,使用 firstName 而不是 fn)。
  • 约定俗成 (Convention): 通常使用驼峰命名法 (camelCase) 来命名局部变量和方法参数,即第一个单词小写,后续单词首字母大写 (如 myVariable, calculateSum)。

var 关键字 (隐式类型)

C# 3.0 引入了 var 关键字,允许你声明一个变量而不显式指定其类型。编译器会根据变量的初始值推断出其类型。这被称为隐式类型

csharp
var age = 30; // 编译器推断 age 的类型是 int
var name = "Alice"; // 编译器推断 name 的类型是 string
var isStudent = true; // 编译器推断 isStudent 的类型是 bool

使用 var 可以让代码更简洁,特别是在类型名很长或者类型可以从初始化表达式中清楚地看出来时。但是,var 只能用于局部变量,并且在声明时必须进行初始化。使用 var 并不意味着变量是弱类型的,一旦类型被推断出来,它就是固定不变的。

数据类型 (Data Types)

数据类型定义了变量可以存储的数据种类、大小以及可以对其执行的操作。C# 是一种强类型语言,这意味着每个变量在声明时都必须有一个明确的类型(或者通过 var 隐式推断),并且类型一旦确定,就不能改变。

C# 的数据类型主要分为两大类:值类型引用类型

  • 值类型 (Value Types): 直接存储数据的值。当你将一个值类型变量赋值给另一个变量时,会创建一个数据的副本。每个值类型变量都有自己的数据副本,它们之间互不影响。值类型通常存储在栈 (Stack) 上。

    • 简单类型: int, double, bool, char 等。
    • 结构体 (struct): 用户自定义的值类型。
    • 枚举 (enum): 用户自定义的包含一组命名常量的值类型。
  • 引用类型 (Reference Types): 存储数据的内存地址,而不是数据本身。当你将一个引用类型变量赋值给另一个变量时,它们指向的是同一块内存区域。修改其中一个变量会影响到另一个。引用类型通常存储在堆 (Heap) 上。

    • 类 (class): 用户自定义的引用类型。
    • 接口 (interface): 定义契约。
    • 委托 (delegate): 用于引用方法的类型。
    • 数组 (array): 存储同类型元素的固定大小的集合(数组本身是引用类型,但可以存储值类型或引用类型)。
    • 字符串 (string): 虽然 string 是一个类(引用类型),但在 C# 中它有很多像值类型一样的使用特性,例如它的不变性 (immutability)。

常用内置数据类型详解

以下是一些你作为初学者会经常遇到的内置数据类型:

  • 整型 (Integer Types): 存储整数,不带小数。根据存储范围和是否带符号,有多种类型。

    • int: 最常用的整型,通常是 32 位带符号整数,范围大约在 ±20 亿。
    • long: 64 位带符号整数,范围更大。
    • short: 16 位带符号整数。
    • byte: 8 位无符号整数,范围 0-255。
      csharp
      int count = 100;
      long bigNumber = 9876543210L; // 后缀 L 表示 long
  • 浮点型 (Floating-Point Types): 存储带小数的数字。

    • double: 最常用的浮点型,双精度 (64 位),精度较高。
    • float: 单精度 (32 位),精度较低,数字后需加 fF 后缀。
    • decimal: 用于金融计算等需要高精度且没有舍入误差的场景,精度最高 (128 位),数字后需加 mM 后缀。
      csharp
      double price = 19.99;
      float temperature = 25.5f;
      decimal salary = 50000.75m;
  • 布尔型 (Boolean Type):

    • bool: 只能存储 truefalse 这两个布尔值。常用于条件判断。
      csharp
      bool isLoggedIn = false;
      bool hasPermission = true;
  • 字符型 (Character Type):

    • char: 存储单个 Unicode 字符,用单引号 ' 包围。
      csharp
      char initial = 'A';
      char symbol = '$';
  • 字符串型 (String Type):

    • string: 存储一系列字符(文本)。用双引号 " 包围。string 是一个引用类型。
      csharp
      string greeting = "Hello";
      string message = "This is a sample text.";

类型安全

C# 的强类型特性意味着一旦变量被声明为某种类型,就不能直接将不兼容类型的值赋给它。例如,你不能直接将一个字符串赋给一个整型变量。这有助于在编译阶段捕获很多错误。

“`csharp
int number = 10;
// string text = number; // 这是错误的,编译会失败!

// 需要进行类型转换 (Type Casting),例如将数字转换为字符串
string text = number.ToString(); // 正确,将 int 转换为 string

// 或者将字符串转换为数字 (如果字符串内容是有效的数字)
string anotherNumberStr = “123”;
int anotherNumber = int.Parse(anotherNumberStr); // 正确 (如果解析失败会抛出异常)
“`
我们将在输入输出部分更详细地讲解类型转换。

运算符 (Operators)

运算符是用于在变量和值上执行操作的符号。

算术运算符

用于执行数学运算:

  • + (加)
  • - (减)
  • * (乘)
  • / (除)
  • % (取模 – 返回除法的余数)

“`csharp
int a = 10;
int b = 5;
int sum = a + b; // 15
int difference = a – b; // 5
int product = a * b; // 50
int quotient = a / b; // 2 (整数除法,丢弃小数部分)
int remainder = a % b; // 0

double x = 10.0;
double y = 4.0;
double result = x / y; // 2.5 (浮点数除法保留小数)
“`

赋值运算符

用于给变量赋值:

  • = (简单赋值)
  • += (加并赋值: x += y 等同于 x = x + y)
  • -= (减并赋值)
  • *= (乘并赋值)
  • /= (除并赋值)
  • %= (取模并赋值)

csharp
int total = 10;
total += 5; // total 现在是 15
total -= 2; // total 现在是 13
total *= 3; // total 现在是 39
total /= 3; // total 现在是 13
total %= 10; // total 现在是 3

比较运算符

用于比较两个值,返回一个布尔值 (truefalse):

  • == (等于)
  • != (不等于)
  • > (大于)
  • < (小于)
  • >= (大于或等于)
  • <= (小于或等于)

csharp
int p = 10;
int q = 20;
bool isEqual = (p == q); // false
bool isNotEqual = (p != q); // true
bool isGreater = (p > q); // false
bool isLess = (p < q); // true

逻辑运算符

用于组合布尔表达式,返回一个布尔值:

  • && (逻辑与 AND): 如果两边的条件都为 true,则结果为 true
  • || (逻辑或 OR): 如果两边的条件至少有一个为 true,则结果为 true
  • ! (逻辑非 NOT): 反转布尔值 (true 变为 falsefalse 变为 true)。

“`csharp
bool condition1 = true;
bool condition2 = false;

bool resultAnd = condition1 && condition2; // false
bool resultOr = condition1 || condition2; // true
bool resultNot = !condition1; // false
``
逻辑与
&&和逻辑或||是短路运算符。对于&&,如果左边的条件为false,则右边的条件不会被评估;对于||,如果左边的条件为true`,则右边的条件不会被评估。

增量/减量运算符

用于将变量的值增加或减少 1:

  • ++ (增量): 将变量值加 1。
  • -- (减量): 将变量值减 1。

这些运算符可以放在变量前 (前缀) 或变量后 (后缀)。
* ++variable (前缀增量): 先将变量值加 1,然后使用新值。
* variable++ (后缀增量): 先使用变量的当前值,然后将变量值加 1。
* --variable (前缀减量): 先将变量值减 1,然后使用新值。
* variable-- (后缀减量): 先使用变量的当前值,然后将变量值减 1。

“`csharp
int i = 5;
int j = ++i; // i 变为 6,j 得到 6
// i 现在是 6,j 现在是 6

int k = 5;
int l = k++; // l 得到 5,然后 k 变为 6
// k 现在是 6,l 现在是 5
``
通常在独立的语句中使用
i++;++i;` 时,前缀和后缀的效果是相同的。区别体现在将增量/减量操作与其他表达式结合使用时。

运算符优先级

当一个表达式中包含多个运算符时,运算符优先级决定了它们的计算顺序。例如,乘除的优先级高于加减。可以使用括号 () 来明确指定计算顺序或覆盖默认优先级。

csharp
int calculation = 10 + 5 * 2; // 先计算 5 * 2 = 10,然后 10 + 10 = 20
int calculationWithParens = (10 + 5) * 2; // 先计算 10 + 5 = 15,然后 15 * 2 = 30

4. 控制流 (Control Flow)

控制流语句允许你根据条件决定代码的执行路径,或者重复执行某段代码。

条件语句 (Conditional Statements)

if 语句

如果指定的条件为 true,则执行 if 代码块内的语句。
csharp
int score = 85;
if (score >= 60)
{
Console.WriteLine("考试及格!");
}

if-else 语句

如果 if 条件为 true,执行 if 块;否则,执行 else 块。
csharp
int score = 45;
if (score >= 60)
{
Console.WriteLine("考试及格!");
}
else
{
Console.WriteLine("考试不及格。");
}

if-else if-else 结构

用于检查多个相关的条件。按顺序检查条件,执行第一个为 true 的条件对应的代码块。如果没有条件为 true,则执行最后的 else 块(如果存在)。
csharp
int score = 75;
if (score >= 90)
{
Console.WriteLine("成绩优秀!");
}
else if (score >= 75)
{
Console.WriteLine("成绩良好。");
}
else if (score >= 60)
{
Console.WriteLine("成绩及格。");
}
else
{
Console.WriteLine("成绩需要努力。");
}

switch 语句

switch 语句提供了一种更简洁的方式来基于一个变量的值选择执行不同的代码块。它通常用于代替多个 else if 语句,当你要将一个变量与多个可能的常量值进行比较时。

“`csharp
int dayOfWeek = 3;
string dayName;

switch (dayOfWeek)
{
case 1:
dayName = “星期一”;
break; // 每个 case 块通常需要以 break 结束
case 2:
dayName = “星期二”;
break;
case 3:
dayName = “星期三”;
break;
case 4:
dayName = “星期四”;
break;
case 5:
dayName = “星期五”;
break;
case 6:
case 7: // 多个 case 可以共用一个代码块
dayName = “周末”;
break;
default: // 如果所有 case 都不匹配,则执行 default 块
dayName = “无效的日期”;
break;
}
Console.WriteLine($”今天是 {dayName}”); // 使用字符串插值 $””
``
注意:在 C# 中,每个
case块必须以break;return;goto case X;throw语句结束,以防止代码“贯穿”(fall-through) 到下一个case,除非该case` 不包含任何语句。

循环语句 (Loop Statements)

循环允许你重复执行一段代码,直到某个条件不再满足。

for 循环

for 循环常用于你知道需要重复执行代码多少次的情况。

结构:for (初始化; 条件; 迭代器) { // 循环体 }

  • 初始化: 循环开始前执行一次,通常用于声明和初始化循环计数器。
  • 条件: 在每次循环迭代开始前检查。如果为 true,继续循环;如果为 false,循环终止。
  • 迭代器: 在每次循环迭代结束后执行,通常用于更新循环计数器。

“`csharp
// 打印数字 0 到 4
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}

// 打印数字 5 到 1 (递减)
for (int j = 5; j > 0; j–)
{
Console.WriteLine(j);
}
“`

while 循环

while 循环在指定的条件为 true 时重复执行代码块。它适用于你不知道循环会执行多少次,只知道终止条件的情况。条件在每次循环开始前检查。

csharp
int count = 0;
while (count < 5)
{
Console.WriteLine(count);
count++; // 确保循环在某个时刻终止,否则会成为无限循环
}

do-while 循环

do-while 循环与 while 类似,但条件在每次循环结束后检查。这意味着 do-while 循环体至少会执行一次,即使条件一开始就是 false

“`csharp
int i = 0;
do
{
Console.WriteLine(i);
i++;
} while (i < 5);

// 即使条件不满足,循环体也会执行一次
int j = 10;
do
{
Console.WriteLine(“这行代码至少会执行一次。”);
} while (j < 5);
“`

foreach 循环

foreach 循环用于遍历集合(如数组、列表等)中的每个元素。它无需使用索引来访问元素,使代码更简洁易读。

“`csharp
string[] fruits = { “Apple”, “Banana”, “Cherry” };

// 遍历数组
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}

// 遍历列表 (后面会介绍 List)
// List numbers = new List { 1, 2, 3, 4, 5 };
// foreach (int number in numbers)
// {
// Console.WriteLine(number);
// }
“`

控制循环流程 (breakcontinue)

  • break: 立即退出当前所在的循环 (for, while, do-while, foreach) 或 switch 语句。
  • continue: 跳过当前循环迭代中剩余的代码,直接进入下一次迭代。

“`csharp
// 使用 break 查找第一个偶数并停止
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
{
Console.WriteLine($”找到第一个偶数: {i}”);
break; // 找到后立即退出循环
}
}

// 使用 continue 跳过奇数
for (int i = 1; i <= 5; i++)
{
if (i % 2 != 0) // 如果是奇数
{
continue; // 跳过本次循环的剩余部分,直接进行下一次迭代
}
Console.WriteLine($”这是一个偶数: {i}”); // 只会打印偶数
}
“`

5. 函数/方法 (Functions/Methods)

方法(在 C# 中通常称为方法 Method)是一段执行特定任务的代码块。使用方法的主要目的是代码重用、组织代码和提高可读性。

为什么使用方法

  • 模块化: 将复杂的程序分解成更小、更易于管理和理解的部分。
  • 代码重用: 一旦定义了一个方法,就可以在程序的多个地方调用它,避免重复编写相同的代码。
  • 易于维护: 如果一个任务的实现需要修改,只需要在一个地方(方法的定义处)修改即可。
  • 提高可读性: 通过有意义的方法名,可以更容易理解程序的逻辑。

方法的定义和结构

方法的定义通常包括以下部分:

csharp
accessModifier static/instance returnType MethodName(parameters)
{
// 方法体:执行任务的代码
// 可能包含 return 语句
}

  • accessModifier: 控制方法的可访问性(例如 public, private)。
  • static/instance:
    • static: 表示该方法属于类本身,可以通过类名直接调用,无需创建类的对象(比如 Console.WriteLine)。
    • instance: 表示该方法属于类的对象实例,需要先创建对象才能调用。
      对于初学者在控制台应用中,经常会看到 static 方法。
  • returnType: 方法执行完毕后返回的数据类型(例如 int, string, bool)。如果方法不返回任何值,则使用关键字 void
  • MethodName: 方法的名称,遵循变量命名规则,通常使用帕斯卡命名法 (PascalCase),即每个单词首字母大写 (如 CalculateSum, PrintMessage)。
  • parameters: 括号 () 内是方法的参数列表。参数用于向方法传递数据。参数列表由参数类型和参数名组成,多个参数之间用逗号 , 分隔。如果没有参数,括号留空。

void 方法 (无返回值)

“`csharp
// 一个简单的 static void 方法
static void Greet(string name) // 接收一个 string 类型的参数 name
{
Console.WriteLine($”你好, {name}!”);
}

// 调用方法
// 在 Main 方法或另一个 static 方法中调用
// Greet(“张三”);
// Greet(“李四”);
“`

带有返回值的方法

“`csharp
// 一个带有 int 返回值和两个 int 参数的 static 方法
static int Add(int num1, int num2)
{
int sum = num1 + num2;
return sum; // 使用 return 关键字返回结果
}

// 调用方法并接收返回值
// int result = Add(5, 3);
// Console.WriteLine($”5 + 3 = {result}”); // 输出 5 + 3 = 8
“`

方法参数

参数允许你在调用方法时向其传递数据。参数可以是任何数据类型。

“`csharp
// 方法有多个参数
static void PrintUserInfo(string name, int age)
{
Console.WriteLine($”姓名: {name}, 年龄: {age}”);
}

// 调用方法
// PrintUserInfo(“王五”, 25);
“`

你还可以在定义方法时为参数指定默认值,这样在调用方法时可以省略带有默认值的参数。

“`csharp
static void GreetOptional(string name, string greeting = “你好”) // greeting 有默认值 “你好”
{
Console.WriteLine($”{greeting}, {name}!”);
}

// 调用时可以省略默认值参数
// GreetOptional(“赵六”); // 输出:你好, 赵六!
// 也可以指定值
// GreetOptional(“钱七”, “Hello”); // 输出:Hello, 钱七!
“`

static 关键字 (初步理解)

在控制台应用程序的 Program 类中,你经常会看到 static 方法和 static 变量。static 成员属于类本身,而不是类的特定实例。这意味着你可以直接通过类名来访问它们,而不需要创建类的对象。

“`csharp
class Calculator // 这是一个类
{
// static 方法属于类,可以直接通过 Calculator.Add 调用
public static int Add(int a, int b)
{
return a + b;
}

// 非 static (实例) 方法属于类的对象,需要先创建对象才能调用
public int Multiply(int a, int b)
{
    return a * b;
}

}

// 在 Main 方法中调用 (Main 方法通常是 static 的)
// int sum = Calculator.Add(10, 20); // 直接通过类名调用 static 方法

// 要调用 Multiply 方法,需要先创建 Calculator 的对象实例
// Calculator calc = new Calculator(); // 创建对象
// int product = calc.Multiply(10, 20); // 通过对象调用实例方法
``
对于初学者,在
Program类中编写简单的工具方法时,通常会将其声明为static,以便于在static Main` 方法中直接调用。

Main 方法:程序的入口

Main 方法是每个 C# 控制台应用程序的必需部分,它是程序执行的起点。一个程序只能有一个入口点 Main 方法。它的典型签名是 static void Main(string[] args) 或简化版的顶级语句形式。

6. 面向对象编程 (OOP) 初探

C# 是一种面向对象的语言。OOP 是一种编程范式,它将程序设计为由“对象”组成,这些对象是数据的属性(字段)和操作数据的行为(方法)的集合。OOP 的核心思想是通过模拟现实世界的对象来组织代码。

OOP 基本概念:类 (Class) 和对象 (Object)

  • 类 (Class): 类是对象的蓝图或模板。它定义了对象的属性(数据)和行为(方法)。类本身不占用内存来存储数据,它只是一个定义。
    例如:Car 类可以定义汽车共同的属性(颜色、品牌、型号)和行为(启动、加速、刹车)。

  • 对象 (Object): 对象是类的实例。根据类的蓝图创建出来的具体实体。每个对象都有自己的属性值。
    例如:根据 Car 类,你可以创建 myCar 对象(红色、丰田、凯美瑞)和 yourCar 对象(蓝色、本田、思域)。

定义一个简单的类

使用 class 关键字定义一个类:

“`csharp
// 定义一个 Person 类
class Person
{
// 类的成员:字段 (属性)
public string Name; // 姓名
public int Age; // 年龄

// 类的成员:方法 (行为)
// 一个让 Person 对象介绍自己的方法
public void IntroduceSelf()
{
    Console.WriteLine($"你好,我叫 {Name},我今年 {Age} 岁。");
}

}
``
这里使用了
public` 访问修饰符,表示这些成员可以从类的外部访问。

创建对象实例

使用 new 关键字根据类创建对象(实例化):

“`csharp
// 在 Main 方法或其他地方
// 创建 Person 类的一个对象实例
Person person1 = new Person();

// 创建 Person 类的另一个对象实例
Person person2 = new Person();
“`

类的成员:字段 (Fields) 和方法 (Methods)

  • 字段 (Fields): 存储对象的数据(属性)。在上面的 Person 类中,NameAge 就是字段。
  • 方法 (Methods): 定义对象的行为或功能。在上面的 Person 类中,IntroduceSelf() 就是一个方法。

访问成员

使用点 . 运算符来访问对象的字段和方法:

“`csharp
// 创建对象
Person person1 = new Person();

// 访问并设置字段的值
person1.Name = “张三”;
person1.Age = 20;

// 访问字段的值
Console.WriteLine($”Person1 的名字是: {person1.Name}”); // 输出:Person1 的名字是: 张三

// 调用对象的方法
person1.IntroduceSelf(); // 输出:你好,我叫 张三,我今年 20 岁。

// 创建另一个对象
Person person2 = new Person();
person2.Name = “李四”;
person2.Age = 22;
person2.IntroduceSelf(); // 输出:你好,我叫 李四,我今年 22 岁。
``
每个对象 (
person1person2) 都有自己独立的NameAge副本,但它们都共享IntroduceSelf` 方法的定义。

属性 (Properties) (初步介绍)

虽然字段可以直接存储数据,但在实际开发中,更推荐使用属性 (Properties) 来访问和控制字段。属性提供了一种灵活的机制来读取 (get) 和写入 (set) 对象的私有字段,可以在 getset 访问器中添加逻辑(如数据验证)。

“`csharp
class PersonWithProperty
{
// 私有字段,通常以下划线开头
private string _name;
private int _age;

// 公共属性,用于访问私有字段
public string Name
{
    get { return _name; } // 读取 _name 字段时执行
    set { _name = value; } // 给 Name 属性赋值时执行,value 是赋过来的值
}

public int Age
{
    get { return _age; }
    set
    {
        // 可以在 set 访问器中添加验证逻辑
        if (value >= 0 && value <= 150)
        {
            _age = value;
        }
        else
        {
            Console.WriteLine("年龄无效!");
        }
    }
}

public void IntroduceSelf()
{
     Console.WriteLine($"你好,我叫 {Name},我今年 {Age} 岁。");
}

}

// 使用带有属性的类
// PersonWithProperty p = new PersonWithProperty();
// p.Name = “王五”; // 调用 Name 属性的 set 访问器
// p.Age = 30; // 调用 Age 属性的 set 访问器
// Console.WriteLine(p.Name); // 调用 Name 属性的 get 访问器
// p.Age = 200; // 年龄无效!
“`
初学者在学习时,可以先从简单的公共字段开始理解,然后逐渐过渡到使用属性,因为属性是 C# 面向对象编程中非常重要的特性。

OOP 的世界远不止于此,还有构造函数、继承、多态、抽象等更高级的概念,但理解类和对象的区别、如何定义类和创建对象是入门的关键。

7. 数组和集合 (Arrays and Collections)

程序经常需要处理一组相关的数据。C# 提供了多种数据结构来存储和管理这些数据,其中最基础的是数组,而 List<T> 是一个非常常用的动态大小的集合。

数组 (Arrays)

数组是存储固定数量同类型元素的序列。数组的元素通过索引访问,索引从 0 开始。

声明和初始化数组

“`csharp
// 声明一个存储整数的数组,但未指定大小和初始值
int[] numbers;

// 声明并指定大小,元素默认为其类型的默认值 (int 默认为 0)
string[] names = new string[5]; // 一个可以存储 5 个字符串的数组

// 声明并初始化,编译器会自动推断大小
double[] temperatures = { 25.5, 28.0, 22.3, 19.8 }; // 大小为 4

// 声明、指定大小并初始化
bool[] statuses = new bool[2] { true, false };
“`

访问数组元素

使用方括号 [] 和元素的索引来访问数组中的元素。

“`csharp
string[] names = new string[3];
names[0] = “Alice”; // 访问第一个元素 (索引 0) 并赋值
names[1] = “Bob”; // 访问第二个元素 (索引 1) 并赋值
names[2] = “Charlie”; // 访问第三个元素 (索引 2) 并赋值

Console.WriteLine(names[1]); // 输出 “Bob”
// Console.WriteLine(names[3]); // 错误!索引越界,数组只有 0, 1, 2 三个索引
``
记住,数组索引总是从 0 开始,到
数组大小 – 1结束。访问超出这个范围的索引会导致运行时错误IndexOutOfRangeException`。

数组的 Length 属性

所有数组都有一个 Length 属性,它返回数组中元素的数量。

csharp
string[] cities = { "New York", "London", "Tokyo" };
int numberOfCities = cities.Length; // numberOfCities 是 3
Console.WriteLine($"城市数量: {numberOfCities}");

遍历数组 (forforeach)

可以使用 for 循环结合索引或 foreach 循环来遍历数组的所有元素。

“`csharp
int[] scores = { 90, 85, 78, 92, 88 };

// 使用 for 循环按索引遍历
Console.WriteLine(“使用 for 循环:”);
for (int i = 0; i < scores.Length; i++)
{
Console.WriteLine($”索引 {i}: {scores[i]}”);
}

// 使用 foreach 循环遍历 (更简洁,无法获取索引)
Console.WriteLine(“\n使用 foreach 循环:”);
foreach (int score in scores)
{
Console.WriteLine(score);
}
“`

列表 (List<T>)

数组的大小在创建后是固定的,这在很多场景下不够灵活。List<T> (泛型列表) 是 .NET 提供的一个非常常用的集合类,它提供了动态大小的功能,并且提供了许多方便的方法来管理元素。T 是一个占位符,表示列表中存储的元素的类型。

要使用 List<T>,你需要引入 System.Collections.Generic 命名空间。

csharp
using System.Collections.Generic; // 添加这行代码

创建和初始化列表

“`csharp
// 创建一个空的整数列表
List ageList = new List();

// 创建一个字符串列表并初始化
List studentNames = new List() { “Alice”, “Bob”, “Charlie” };
“`

添加、访问和移除元素

List<T> 提供了许多实用的方法:

  • Add(element): 在列表末尾添加一个元素。
  • AddRange(collection): 添加一个集合的所有元素。
  • Insert(index, element): 在指定索引处插入一个元素。
  • Remove(element): 移除列表中第一次出现的指定元素。
  • RemoveAt(index): 移除指定索引处的元素。
  • Clear(): 移除列表中的所有元素。
  • Contains(element): 检查列表是否包含指定元素,返回 bool

访问元素与数组类似,也是通过索引:listName[index]

“`csharp
List fruits = new List();

fruits.Add(“Apple”); // [“Apple”]
fruits.Add(“Banana”); // [“Apple”, “Banana”]
fruits.Add(“Cherry”); // [“Apple”, “Banana”, “Cherry”]

Console.WriteLine($”第一个水果: {fruits[0]}”); // 输出 “Apple”

fruits.Insert(1, “Orange”); // [“Apple”, “Orange”, “Banana”, “Cherry”]

fruits.Remove(“Banana”); // [“Apple”, “Orange”, “Cherry”]

fruits.RemoveAt(0); // [“Orange”, “Cherry”]

bool hasGrape = fruits.Contains(“Grape”); // false
bool hasCherry = fruits.Contains(“Cherry”); // true

Console.WriteLine($”当前列表中的水果数量: {fruits.Count}”); // Count 属性获取元素数量
“`

列表的 Count 属性

List<T> 使用 Count 属性来获取列表中元素的数量,而不是数组的 Length

csharp
List<int> numbers = new List<int>() { 1, 2, 3 };
Console.WriteLine($"列表大小: {numbers.Count}"); // 输出 3

遍历列表

foreach 循环是遍历列表最常用的方式:

csharp
List<string> colors = new List<string>() { "Red", "Green", "Blue" };
foreach (string color in colors)
{
Console.WriteLine(color);
}

你也可以使用 for 循环结合索引和 colors.Count 来遍历。

数组与列表的选择

  • 如果需要存储固定数量的同类型元素,并且对性能要求较高(直接通过索引访问效率高),可以选择数组
  • 如果需要存储可变数量的同类型元素,并且需要方便地添加、删除和查找元素,List 通常是更好的选择。

对于初学者,在不确定大小时,List<T> 往往更灵活方便。

8. 控制台输入和输出 (Console I/O)

System.Console 类提供了与控制台窗口进行交互的方法,包括读取用户的输入和向控制台输出文本。

向控制台输出 (Console.Write, Console.WriteLine)

  • Console.WriteLine(output): 输出指定的文本,并在末尾添加换行符。
  • Console.Write(output): 输出指定的文本,不添加换行符。

你可以输出各种数据类型,C# 会自动将它们转换为字符串进行显示。

“`csharp
Console.WriteLine(“这是一行文本。”);
Console.Write(“这是”);
Console.Write(“另一行文本,”);
Console.WriteLine(“但它会和前面连在一起。”); // 输出: 这是另一行文本,但它会和前面连在一起。

int age = 30;
Console.WriteLine(“年龄: ” + age); // 使用字符串连接 + 运算符
Console.WriteLine($”年龄: {age}”); // 使用字符串插值 $”” (推荐,更清晰)
“`

从控制台读取输入 (Console.ReadLine)

Console.ReadLine() 方法从控制台读取用户输入的一整行文本,直到用户按下 Enter 键,并返回一个 string 类型的值。

“`csharp
Console.Write(“请输入你的名字: “);
string userName = Console.ReadLine(); // 读取用户输入并存储到 userName 变量

Console.WriteLine($”你好, {userName}!”);
“`

字符串与数字之间的转换

Console.ReadLine() 总是返回一个字符串。如果你需要将用户输入的数字进行计算,就需要将字符串转换为相应的数值类型。

  • int.Parse(string) / double.Parse(string) 等: 将字符串转换为对应的数值类型。如果字符串不能转换为有效的数字,会抛出 FormatException 异常。
  • Convert.ToInt32(string) / Convert.ToDouble(string) 等: 也可以用于转换,功能类似 Parse,但 Convert 类还支持更多类型之间的转换,并且可以处理 null (将其转换为类型的默认值,如 int 的 0)。如果转换失败也会抛出异常。
  • int.TryParse(string, out result) / double.TryParse(string, out result) 等: 这是更安全的方式。它尝试将字符串转换为指定的数值类型。如果转换成功,返回 true,并将转换结果存储在 out 参数 result 中;如果转换失败,返回 false,而不会抛出异常。在处理用户输入时强烈推荐使用 TryParse

“`csharp
Console.Write(“请输入一个整数年龄: “);
string ageString = Console.ReadLine();

// 使用 int.Parse (不安全,输入非数字会报错)
// int age = int.Parse(ageString);
// Console.WriteLine($”你的年龄是: {age}”);

// 使用 int.TryParse (推荐)
int age;
bool isNumeric = int.TryParse(ageString, out age); // 尝试转换,结果存在 age 变量中

if (isNumeric)
{
Console.WriteLine($”你的年龄是: {age}”);
}
else
{
Console.WriteLine(“输入无效,请输入一个有效的整数。”);
}
``out关键字表示age` 是一个输出参数,方法可以在内部给它赋值,并在方法返回后保留其值。

类似地,你也可以将数字转换回字符串以便输出或其他用途,最简单的方法是使用 ToString() 方法:

csharp
int number = 123;
string numberString = number.ToString(); // numberString 现在是 "123"

9. 错误处理 (Error Handling)

在程序运行时,可能会发生各种错误,例如尝试除以零、访问不存在的文件、将无效格式的字符串转换为数字等。这些运行时错误称为异常 (Exceptions)。如果不对异常进行处理,程序会崩溃。

使用 try-catch 块可以捕获并处理异常,使程序更加健壮。

try-catch

  • try 块中放置可能会抛出异常的代码。
  • catch 块用于捕获并处理 try 块中发生的特定类型或任何类型的异常。

“`csharp
try
{
// 尝试执行可能抛出异常的代码
Console.Write(“请输入一个数字: “);
string input = Console.ReadLine();
int number = int.Parse(input); // 这行代码可能抛出 FormatException

Console.Write("请输入另一个数字 (非零): ");
string input2 = Console.ReadLine();
int number2 = int.Parse(input2);

int result = number / number2; // 这行代码可能抛出 DivideByZeroException

Console.WriteLine($"结果是: {result}");

}
catch (FormatException ex) // 捕获 FormatException 类型的异常
{
// 异常处理代码:当发生 FormatException 时执行
Console.WriteLine(“错误:请输入有效的数字!”);
// Console.WriteLine($”详细错误信息: {ex.Message}”); // 可以打印异常消息
}
catch (DivideByZeroException ex) // 捕获 DivideByZeroException 类型的异常
{
Console.WriteLine(“错误:除数不能为零!”);
}
catch (Exception ex) // 捕获所有其他类型的异常 (通用捕获块应放在最后)
{
Console.WriteLine(“发生了未知错误!”);
// Console.WriteLine($”详细错误信息: {ex.Message}”);
}
// 还可以添加 finally 块,无论是否发生异常都会执行
// finally
// {
// Console.WriteLine(“错误处理结束。”);
// }

Console.WriteLine(“程序继续执行。”); // 如果异常被捕获,程序不会崩溃,会继续执行这里
“`

try 块中的代码执行时:
* 如果没有发生异常,catch 块会被跳过。
* 如果发生异常,try 块中剩余的代码会被跳过,程序会跳转到匹配该异常类型的 catch 块执行。
* 如果在 try 块中发生了异常,并且没有匹配的 catch 块,程序将会终止并报告未处理的异常。

捕获特定类型的异常(如 FormatException)比捕获通用的 Exception 更精确,可以针对不同类型的错误采取不同的处理措施。如果使用多个 catch 块,应该将更具体的异常类型放在前面,通用的 Exception 放在最后。

10. 接下来是什么?

恭喜你!你已经掌握了 C# 的核心基础知识,包括:

  • 环境设置和第一个程序
  • 变量、数据类型和运算符
  • 条件判断和循环
  • 方法的定义和调用
  • 类和对象的初步概念
  • 数组和列表的使用
  • 控制台输入输出
  • 基本的错误处理

这只是 C# 世界的冰山一角。要成为一名熟练的 C# 开发者,你还需要继续深入学习:

  • 深入学习 OOP: 掌握构造函数、继承、多态、抽象类、接口、封装、访问修饰符等。
  • 泛型 (Generics): 编写类型安全且可重用的代码(如 List<T>)。
  • 委托 (Delegates) 和事件 (Events): 用于构建松耦合的系统。
  • Lambda 表达式和 LINQ: 强大的数据查询和处理工具。
  • 文件和流操作 (File I/O): 读写文件。
  • 异步编程 (Async/Await): 处理耗时操作,保持应用程序响应。
  • 命名空间和程序集 (Namespaces and Assemblies): 更好地组织和管理大型项目代码。
  • NuGet 包管理器: 利用社区提供的各种库和工具。
  • 单元测试: 确保代码的正确性。

掌握了 C# 基础后,你可以选择一个感兴趣的方向深入探索:

  • 桌面开发: WPF, Windows Forms, .NET MAUI
  • Web 开发: ASP.NET Core MVC, Blazor
  • 游戏开发: Unity
  • 云开发: Azure Functions, Azure App Service
  • 数据访问: ADO.NET, Entity Framework Core

最重要的是:持续实践! 尝试编写更多小程序,解决实际问题,参与开源项目,或者跟着更高级的教程学习。

11. 总结与鼓励

C# 是一门功能强大、用途广泛的语言,学会它将为你打开广阔的职业发展道路。本教程为你构建了一个坚实的 C# 基础框架。

编程是一项需要不断学习和实践的技能。在学习过程中遇到困难是正常的,不要气馁。多查阅官方文档 (Microsoft Docs)、搜索在线资源、参与开发者社区讨论,这些都能帮助你解决问题。

祝你在 C# 的学习旅程中一切顺利!现在,开始编写你的下一行 C# 代码吧!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部