C# 是一种现代、通用的编程语言,它由微软在2000年发布,作为.NET框架的一部分。C# 是一种面向对象的语言,结合了C++的高性能和Visual Basic的易用性。本文将详细介绍C#的基础语法,从数据类型、变量、运算符到控制结构、方法、类和对象等,并涵盖注释方式、命名规则、异常处理等重要内容,帮助读者全面掌握C#的基础知识。

引言

C# 作为一种现代编程语言,以其简洁、强大和高效的特性,成为许多开发者的首选语言之一。它不仅适用于桌面应用开发,还广泛用于Web应用、游戏开发、移动应用等领域。本篇文章将通过详细的讲解和丰富的代码示例,带领读者深入理解C#的基础语法和编程技巧。

数据类型和变量

C# 提供了丰富的数据类型,以满足不同的编程需求。这些数据类型主要分为两类:值类型和值类型。

值类型

值类型直接存储数据,存储在栈中。C# 的基本值类型包括:

  • int: 代表整数类型,如 int age = 25;
  • float: 代表单精度浮点数,如 float temperature = 36.6f;
  • double: 代表双精度浮点数,如 double pi = 3.14159;
  • char: 代表单个字符,如 char grade = 'A';
  • bool: 代表布尔值,如 bool isStudent = true;

引用类型

引用类型存储的是对象的引用,存储在堆中。常见的引用类型包括:

  • string: 代表字符串,如 string name = "Alice";
  • object: 所有类型的基类,如 object obj = 42;
  • 类(class)
  • 接口(interface)
  • 数组(array)

变量声明和初始化

在C#中,变量声明时必须指定类型,且可以在声明的同时进行初始化:

  1. int number = 10;
  2. float price = 19.99f;
  3. string message = "Hello, World!";

注释

注释是程序员用来解释代码的文本,不会被编译器执行。C# 支持单行注释和多行注释两种方式。

单行注释

使用 // 符号表示单行注释:

  1. // 这是一个单行注释
  2. int number = 42; // 声明一个整型变量

多行注释

使用 /* */ 符号表示多行注释:

  1. /* 这是一个多行注释
  2. 它可以跨越多行 */
  3. int number = 42; /* 声明一个整型变量 */

运算符

运算符是用于执行各种操作的符号。C# 中的运算符分为以下几类:

算术运算符

用于执行基本的数学运算:

  • +: 加法,如 int sum = a + b;
  • -: 减法,如 int difference = a - b;
  • *: 乘法,如 int product = a * b;
  • /: 除法,如 int quotient = a / b;
  • %: 取余,如 int remainder = a % b;

关系运算符

用于比较两个值之间的关系:

  • ==: 等于,如 if (a == b) { }
  • !=: 不等于,如 if (a != b) { }
  • >: 大于,如 if (a > b) { }
  • <: 小于,如 if (a < b) { }
  • >=: 大于或等于,如 if (a >= b) { }
  • <=: 小于或等于,如 if (a <= b) { }

逻辑运算符

用于执行逻辑操作:

  • &&: 逻辑与,如 if (a > 0 && b > 0) { }
  • ||: 逻辑或,如 if (a > 0 || b > 0) { }
  • !: 逻辑非,如 if (!flag) { }

赋值运算符

用于给变量赋值:

  • =: 赋值,如 int a = 10;
  • +=: 加法赋值,如 a += 5; 等价于 a = a + 5;
  • -=: 减法赋值,如 a -= 5; 等价于 a = a - 5;
  • *=: 乘法赋值,如 a *= 5; 等价于 a = a * 5;
  • /=: 除法赋值,如 a /= 5; 等价于 a = a / 5;
  • %=: 取余赋值,如 a %= 5; 等价于 a = a % 5;

位运算符

用于对二进制位进行操作:

  • &: 按位与,如 a & b
  • |: 按位或,如 a | b
  • ^: 按位异或,如 a ^ b
  • ~: 按位取反,如 ~a
  • <<: 左移,如 a << 2
  • >>: 右移,如 a >> 2

控制结构

控制结构用于控制程序的执行流程,包括条件语句和循环语句。

条件语句

条件语句根据表达式的真假来决定执行的代码块。

if 语句

根据条件表达式的值来执行相应的代码块:

  1. int number = 10;
  2. if (number > 0)
  3. {
  4. Console.WriteLine("Positive number");
  5. }
  6. else if (number < 0)
  7. {
  8. Console.WriteLine("Negative number");
  9. }
  10. else
  11. {
  12. Console.WriteLine("Zero");
  13. }
switch 语句

用于在多个可能的选项中选择一个执行:

  1. int day = 3;
  2. switch (day)
  3. {
  4. case 1:
  5. Console.WriteLine("Monday");
  6. break;
  7. case 2:
  8. Console.WriteLine("Tuesday");
  9. break;
  10. case 3:
  11. Console.WriteLine("Wednesday");
  12. break;
  13. default:
  14. Console.WriteLine("Invalid day");
  15. break;
  16. }

循环语句

循环语句用于重复执行一段代码。

for 循环

用于执行已知次数的循环:

  1. for (int i = 0; i < 10; i++)
  2. {
  3. Console.WriteLine(i);
  4. }
while 循环

用于执行未知次数的循环,直到条件表达式为假:

  1. int i = 0;
  2. while (i < 10)
  3. {
  4. Console.WriteLine(i);
  5. i++;
  6. }
do-while 循环

至少执行一次循环,然后根据条件表达式决定是否继续执行:

  1. int i = 0;
  2. do
  3. {
  4. Console.WriteLine(i);
  5. i++;
  6. } while (i < 10);
foreach 循环

用于遍历集合中的元素:

  1. int[] numbers = { 1, 2, 3, 4, 5 };
  2. foreach (int number in numbers)
  3. {
  4. Console.WriteLine(number);
  5. }

方法

方法是执行特定任务的代码块。C# 的方法具有返回类型、名称、参数列表和主体。

方法声明

一个简单的方法示例如下:

  1. public int Add(int a, int b)
  2. {
  3. return a + b;
  4. }

调用方法:

  1. int result = Add(5, 10);
  2. Console.WriteLine(result); // 输出15

方法重载

方法重载是指在同一个类中可以定义多个具有相同名称但参数不同的方法:

  1. public int Add(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. public double Add(double a, double b)
  6. {
  7. return a + b;
  8. }

调用方法:

  1. int result1 = Add(5, 10); // 调用第一个Add方法
  2. double result2 = Add(5.5, 10.5); // 调用第二个Add方法

可选参数

C# 支持为方法定义可选参数,如果调用方法时没有提供这些参数,则使用默认值:

  1. public void PrintMessage(string message = "Hello, World!")
  2. {
  3. Console.WriteLine(message);
  4. }
  5. PrintMessage(); // 输出 "Hello, World!"
  6. PrintMessage("Hi there!"); // 输出 "Hi there!"

类和对象

C# 是一种面向对象的编程语言,类和对象是其核心概念。

类的定义

类是对象的蓝图,定义了对象的属性和行为:

  1. public class Person
  2. {
  3. // 属性
  4. public string Name { get; set; }
  5. public int Age { get; set; }
  6. // 构造函数
  7. public Person(string name, int age)
  8. {
  9. Name = name;
  10. Age = age;
  11. }
  12. // 方法
  13. public void Introduce()
  14. {
  15. Console.WriteLine($"Hi, I'm {Name} and I'm {Age} years old.");
  16. }
  17. }

创建对象

对象是类的实例,通过 new 关键字创建:

  1. Person person = new Person("Alice", 30);
  2. person.Introduce(); // 输出 "Hi, I'm Alice and I'm 30 years old."

继承

继承是面向对象编程的重要特性,允许一个类从另一个类继承属性和方法:

  1. public class Employee : Person
  2. {
  3. public string Company { get; set; }
  4. public Employee(string name, int age, string company)
  5. : base(name, age)
  6. {
  7. Company = company;
  8. }
  9. public void Work()
  10. {
  11. Console.WriteLine($"{Name} is working at {Company}.");
  12. }
  13. }
  14. Employee employee = new Employee("Bob", 35, "ACME Corp");
  15. employee.Introduce(); // 输出 "Hi, I'm Bob and I'm 35 years old."
  16. employee.Work(); // 输出 "Bob is working at ACME Corp."

多态

多态允许使用基类的引用来调用子类的方法:

  1. public class Animal
  2. {
  3. public virtual void Speak()
  4. {
  5. Console.WriteLine("Animal speaks.");
  6. }
  7. }
  8. public class Dog : Animal
  9. {
  10. public override void Speak()
  11. {
  12. Console.WriteLine("Dog barks.");
  13. }
  14. }
  15. public class Cat : Animal
  16. {
  17. public override void Speak()
  18. {
  19. Console.WriteLine("Cat meows.");
  20. }
  21. }
  22. Animal myDog = new Dog();
  23. Animal myCat = new Cat();
  24. myDog.Speak(); // 输出 "Dog barks."
  25. myCat.Speak(); // 输出 "Cat meows."

命名规则

良好的命名规则可以提高代码的可读性和可维护性。C# 通常遵循以下命名规则:

  • 类名和接口名使用 Pascal 大小写,例如 PersonIShape
  • 方法名、属性名和事件名使用 Pascal 大小写,例如 GetNameCalculateArea
  • 变量名和参数名使用 camel 大小写,例如 firstNametotalAmount
  • 常量名使用全大写字母和下划线,例如 MAX_VALUE

数组

数组是一种用于存储相同类型数据的集合。

一维数组

声明和初始化一维数组:

  1. int[] numbers = new int[5];
  2. numbers[0] = 1;
  3. numbers[1] = 2;
  4. // 或者
  5. int[] numbers = { 1, 2, 3, 4, 5 };

遍历数组:

  1. foreach (int number in numbers)
  2. {
  3. Console.WriteLine(number);
  4. }

多维数组

声明和初始化多维数组:

  1. int[,] matrix = new int[2, 3];
  2. matrix[0, 0] = 1;
  3. matrix[0, 1] = 2;
  4. matrix[0, 2] = 3;
  5. // 或者
  6. int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 } };

遍历多维数组:

  1. for (int i = 0; i < matrix.GetLength(0); i++)
  2. {
  3. for (int j = 0; j < matrix.GetLength(1); j++)
  4. {
  5. Console.WriteLine(matrix[i, j]);
  6. }
  7. }

交错数组

声明和初始化交错数组:

  1. int[][] jaggedArray = new int[2][];
  2. jaggedArray[0] = new int[] { 1, 2, 3 };
  3. jaggedArray[1] = new int[] { 4, 5 };
  4. foreach (int[] array in jaggedArray)
  5. {
  6. foreach (int number in array)
  7. {
  8. Console.WriteLine(number);
  9. }
  10. }

字符串

字符串是字符的集合,在C#中是引用类型。

字符串操作

字符串的常见操作包括连接、比较、查找、替换等。

字符串连接

使用 + 运算符或 String.Concat 方法连接字符串:

  1. string firstName = "John";
  2. string lastName = "Doe";
  3. string fullName = firstName + " " + lastName;
  4. // 或者
  5. string fullName = String.Concat(firstName, " ", lastName);
字符串插值

使用 $ 字符和花括号 {} 进行字符串插值:

  1. string name = "Alice";
  2. int age = 25;
  3. string message = $"Hi, I'm {name} and I'm {age} years old.";
字符串比较

使用 == 运算符或 String.Compare 方法比较字符串:

  1. string str1 = "Hello";
  2. string str2 = "hello";
  3. bool areEqual = str1 == str2; // false
  4. int comparison = String.Compare(str1, str2, true); // 0 (忽略大小写)
字符串查找

使用 String.IndexOf 方法查找子字符串的位置:

  1. string message = "Hello, world!";
  2. int index = message.IndexOf("world"); // 7
字符串替换

使用 String.Replace 方法替换子字符串:

  1. string message = "Hello, world!";
  2. string newMessage = message.Replace("world", "C#"); // "Hello, C#!"

字符串常用方法

C# 提供了丰富的字符串处理方法,例如 SubstringSplitTrim 等。

Substring

从字符串中提取子字符串:

  1. string message = "Hello, world!";
  2. string hello = message.Substring(0, 5); // "Hello"
Split

根据指定的分隔符拆分字符串:

  1. string message = "apple,banana,cherry";
  2. string[] fruits = message.Split(','); // {"apple", "banana", "cherry"}
Trim

移除字符串开头和结尾的空白字符:

  1. string message = " Hello, world! ";
  2. string trimmedMessage = message.Trim(); // "Hello, world!"

集合

集合用于存储和管理一组相关的对象。C# 提供了多种集合类型,如列表、字典、队列和栈。

List

List<T> 是一种动态数组,可以存储任意类型的对象,并提供了丰富的方法来操作这些对象。

创建和初始化列表

创建和初始化一个列表:

  1. List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
添加元素

使用 Add 方法向列表添加元素:

  1. numbers.Add(6);
访问元素

使用索引访问列表中的元素:

  1. int firstNumber = numbers[0]; // 1
遍历列表

使用 foreach 循环遍历列表中的元素:

  1. foreach (int number in numbers)
  2. {
  3. Console.WriteLine(number);
  4. }

Dictionary

Dictionary<TKey, TValue> 是一种键值对集合,用于存储具有唯一键的对象。

创建和初始化字典

创建和初始化一个字典:

  1. Dictionary<string, int> ages = new Dictionary<string, int>
  2. {
  3. { "Alice", 25 },
  4. { "Bob", 30 }
  5. };
添加元素

使用 Add 方法向字典添加键值对:

  1. ages.Add("Charlie", 35);
访问元素

使用键访问字典中的值:

  1. int aliceAge = ages["Alice"]; // 25
遍历字典

使用 foreach 循环遍历字典中的键值对:

  1. foreach (KeyValuePair<string, int> entry in ages)
  2. {
  3. Console.WriteLine($"{entry.Key}: {entry.Value}");
  4. }

Queue

Queue<T> 是一种先进先出(FIFO)的集合,用于按顺序存储元素。

创建和初始化队列

创建和初始化一个队列:

  1. Queue<string> queue = new Queue<string>();
添加元素

使用 Enqueue 方法向队列添加元素:

  1. queue.Enqueue("Alice");
  2. queue.Enqueue("Bob");
移除和访问元素

使用 Dequeue 方法移除并返回队列中的第一个元素:

  1. string first = queue.Dequeue(); // "Alice"
访问队列头部元素

使用 Peek 方法访问但不移除队列中的第一个元素:

  1. string next = queue.Peek(); // "Bob"

Stack

Stack<T> 是一种后进先出(LIFO)的集合,用于按顺序存储元素。

创建和初始化

栈 创建和初始化一个栈:

  1. Stack<string> stack = new Stack<string>();
添加元素

使用 Push 方法向栈添加元素:

  1. stack.Push("Alice");
  2. stack.Push("Bob");
移除和访问元素

使用 Pop 方法移除并返回栈顶元素:

  1. string top = stack.Pop(); // "Bob"
访问栈顶元素

使用 Peek 方法访问但不移除栈顶元素:

  1. string next = stack.Peek(); // "Alice"

异常处理

异常处理是指在程序运行过程中捕获和处理异常,以防止程序崩溃并提供有意义的错误信息。

try-catch 语句

使用 try-catch 语句捕获和处理异常:

  1. try
  2. {
  3. int result = 10 / 0;
  4. }
  5. catch (DivideByZeroException ex)
  6. {
  7. Console.WriteLine("Cannot divide by zero.");
  8. }
  9. catch (Exception ex)
  10. {
  11. Console.WriteLine("An error occurred: " + ex.Message);
  12. }

finally 语句

finally 语句用于执行无论是否发生异常都要执行的代码:

  1. try
  2. {
  3. int result = 10 / 0;
  4. }
  5. catch (DivideByZeroException ex)
  6. {
  7. Console.WriteLine("Cannot divide by zero.");
  8. }
  9. finally
  10. {
  11. Console.WriteLine("Execution completed.");
  12. }

抛出异常

使用 throw 关键字显式抛出异常:

  1. public void Divide(int a, int b)
  2. {
  3. if (b == 0)
  4. {
  5. throw new DivideByZeroException("Divisor cannot be zero.");
  6. }
  7. Console.WriteLine(a / b);
  8. }
  9. try
  10. {
  11. Divide(10, 0);
  12. }
  13. catch (DivideByZeroException ex)
  14. {
  15. Console.WriteLine(ex.Message);
  16. }

文件操作

C# 提供了丰富的文件操作类,用于读取和写入文件。

读取文件

使用 StreamReader 读取文件内容:

  1. using System.IO;
  2. string path = "example.txt";
  3. using (StreamReader reader = new StreamReader(path))
  4. {
  5. string content = reader.ReadToEnd();
  6. Console.WriteLine(content);
  7. }

写入文件

使用 StreamWriter 写入文件内容:

  1. using System.IO;
  2. string path = "example.txt";
  3. string content = "Hello, world!";
  4. using (StreamWriter writer = new StreamWriter(path))
  5. {
  6. writer.WriteLine(content);
  7. }

文件存在性检查

使用 File.Exists 方法检查文件是否存在:

  1. string path = "example.txt";
  2. if (File.Exists(path))
  3. {
  4. Console.WriteLine("File exists.");
  5. }
  6. else
  7. {
  8. Console.WriteLine("File does not exist.");
  9. }

异步编程

异步编程是一种编程模式,允许程序在等待某些操作完成时不阻塞主线程。

async 和 await 关键字

使用 asyncawait 关键字编写异步方法:

  1. using System.Net.Http;
  2. using System.Threading.Tasks;
  3. public async Task<string> FetchDataAsync(string url)
  4. {
  5. using (HttpClient client = new HttpClient())
  6. {
  7. string result = await client.GetStringAsync(url);
  8. return result;
  9. }
  10. }
  11. async Task Main()
  12. {
  13. string data = await FetchDataAsync("http://example.com");
  14. Console.WriteLine(data);
  15. }

Task 类

使用 Task 类表示异步操作:

  1. public async Task<int> ComputeAsync(int a, int b)
  2. {
  3. return await Task.Run(() =>
  4. {
  5. // 模拟长时间计算
  6. System.Threading.Thread.Sleep(2000);
  7. return a + b;
  8. });
  9. }
  10. async Task Main()
  11. {
  12. int result = await ComputeAsync(5, 10);
  13. Console.WriteLine(result); // 输出15
  14. }

事件

事件是一种用于在对象之间传递消息的机制,常用于实现事件驱动编程。

定义事件

使用 event 关键字定义事件:

  1. public class Alarm
  2. {
  3. public event EventHandler Ring;
  4. public void Trigger()
  5. {
  6. if (Ring != null)
  7. {
  8. Ring(this, EventArgs.Empty);
  9. }
  10. }
  11. }

订阅和触发事件

使用 += 运算符订阅事件,使用 -= 运算符取消订阅:

  1. public class Program
  2. {
  3. static void Main()
  4. {
  5. Alarm alarm = new Alarm();
  6. alarm.Ring += Alarm_Ring;
  7. alarm.Trigger();
  8. }
  9. private static void Alarm_Ring(object sender, EventArgs e)
  10. {
  11. Console.WriteLine("Alarm triggered!");
  12. }
  13. }

委托

委托是C#中的一种类型安全的函数指针,允许你将方法作为参数传递。

定义委托

使用 delegate 关键字定义委托:

  1. public delegate void Notify(string message);

使用委托

创建委托实例并调用方法:

  1. public class Program
  2. {
  3. public static void Main()
  4. {
  5. Notify notify = new Notify(ShowMessage);
  6. notify("Hello, delegates!");
  7. }
  8. public static void ShowMessage(string message)
  9. {
  10. Console.WriteLine(message);
  11. }
  12. }

多播委托

多播委托可以指向多个方法,并依次调用这些方法:

  1. public delegate void Notify(string message);
  2. public class Program
  3. {
  4. public static void Main()
  5. {
  6. Notify notify = ShowMessage;
  7. notify += ShowAnotherMessage;
  8. notify("Hello, delegates!");
  9. }
  10. public static void ShowMessage(string message)
  11. {
  12. Console.WriteLine("Message: " + message);
  13. }
  14. public static void ShowAnotherMessage(string message)
  15. {
  16. Console.WriteLine("Another message: " + message);
  17. }
  18. }

Lambda 表达式

Lambda 表达式是一种简洁的匿名函数语法,常用于简化委托和 LINQ 表达式。

定义 Lambda 表达式

使用 => 运算符定义 Lambda 表达式:

  1. Func<int, int, int> add = (a, b) => a + b;
  2. int result = add(5, 10); // 输出15

Lambda 表达式与 LINQ

结合 Lambda 表达式和 LINQ 查询数据:

  1. int[] numbers = { 1, 2, 3, 4, 5 };
  2. var evenNumbers = numbers.Where(n => n % 2 == 0);
  3. foreach (int number in evenNumbers)
  4. {
  5. Console.WriteLine(number); // 输出2 4
  6. }

LINQ

语言集成查询(LINQ)是一种查询数据的功能,允许你使用类似SQL的语法来查询集合。

LINQ 查询语法

使用 LINQ 查询语法查询数据:

  1. int[] numbers = { 1, 2, 3, 4, 5 };
  2. var evenNumbers = from number in numbers
  3. where number % 2 == 0
  4. select number;
  5. foreach (int number in evenNumbers)
  6. {
  7. Console.WriteLine(number); // 输出2 4
  8. }

LINQ 方法语法

使用 LINQ 方法语法查询数据:

  1. int[] numbers = { 1, 2, 3, 4, 5 };
  2. var evenNumbers = numbers.Where(n => n % 2 == 0).Select(n => n);
  3. foreach (int number in evenNumbers)
  4. {
  5. Console.WriteLine(number); // 输出2 4
  6. }

属性

属性是类中的变量,通过访问器(getter 和 setter)来控制对这些变量的访问。

自动属性

使用自动属性简化属性定义:

  1. public class Person
  2. {
  3. public string Name { get; set; }
  4. public int Age { get; set; }
  5. }

手动属性

使用手动属性定义带有访问器的属性:

  1. public class Person
  2. {
  3. private int age;
  4. public int Age
  5. {
  6. get { return age; }
  7. set
  8. {
  9. if (value >= 0)
  10. {
  11. age = value;
  12. }
  13. }
  14. }
  15. }

索引器

索引器允许类像数组一样通过索引访问其内部数据。

定义索引器

使用 this 关键字定义索引器:

  1. public class DataCollection
  2. {
  3. private int[] data = new int[10];
  4. public int this[int index]
  5. {
  6. get { return data[index]; }
  7. set { data[index] = value; }
  8. }
  9. }

使用索引器

创建对象并通过索引器访问数据:

  1. DataCollection collection = new DataCollection();
  2. collection[0] = 42;
  3. int value = collection[0]; // 42

接口

接口定义了一组方法和属性的规范,而不提供实现。

定义接口

使用 interface 关键字定义接口:

  1. public interface IMovable
  2. {
  3. void Move();
  4. }

实现接口

使用 : 符号实现接口:

  1. public class Car :
  2. IMovable
  3. {
  4. public void Move()
  5. {
  6. Console.WriteLine("Car is moving.");
  7. }
  8. }

接口多重继承

一个类可以实现多个接口:

  1. public interface IFlyable
  2. {
  3. void Fly();
  4. }
  5. public class FlyingCar : IMovable, IFlyable
  6. {
  7. public void Move()
  8. {
  9. Console.WriteLine("FlyingCar is moving.");
  10. }
  11. public void Fly()
  12. {
  13. Console.WriteLine("FlyingCar is flying.");
  14. }
  15. }

抽象类

抽象类是不能被实例化的类,通常用于作为其他类的基类。

定义抽象类

使用 abstract 关键字定义抽象类和抽象方法:

  1. public abstract class Animal
  2. {
  3. public abstract void Speak();
  4. }

继承抽象类

使用 override 关键字重写抽象方法:

  1. public class Dog : Animal
  2. {
  3. public override void Speak()
  4. {
  5. Console.WriteLine("Dog barks.");
  6. }
  7. }
  8. public class Cat : Animal
  9. {
  10. public override void Speak()
  11. {
  12. Console.WriteLine("Cat meows.");
  13. }
  14. }

枚举

枚举是定义一组命名常量的类型,通常用于表示一组相关的值。

定义枚举

使用 enum 关键字定义枚举:

  1. public enum DaysOfWeek
  2. {
  3. Sunday,
  4. Monday,
  5. Tuesday,
  6. Wednesday,
  7. Thursday,
  8. Friday,
  9. Saturday
  10. }

使用枚举

创建枚举变量并赋值:

  1. DaysOfWeek today = DaysOfWeek.Monday;
  2. if (today == DaysOfWeek.Monday)
  3. {
  4. Console.WriteLine("Today is Monday.");
  5. }

嵌套类

嵌套类是定义在另一个类内部的类,用于组织相关的类。

定义嵌套类

在外部类内部定义嵌套类:

  1. public class OuterClass
  2. {
  3. public class NestedClass
  4. {
  5. public void Display()
  6. {
  7. Console.WriteLine("This is a nested class.");
  8. }
  9. }
  10. }

使用嵌套类

创建嵌套类的实例并调用其方法:

  1. OuterClass.NestedClass nested = new OuterClass.NestedClass();
  2. nested.Display(); // 输出 "This is a nested class."

泛型

泛型允许定义类、接口和方法时使用类型参数,从而使代码更加通用和类型安全。

泛型类

定义泛型类:

  1. public class GenericList<T>
  2. {
  3. private T[] items = new T[100];
  4. private int count = 0;
  5. public void Add(T item)
  6. {
  7. items[count++] = item;
  8. }
  9. public T Get(int index)
  10. {
  11. return items[index];
  12. }
  13. }

泛型方法

定义泛型方法:

  1. public class Utilities
  2. {
  3. public T Max<T>(T a, T b) where T : IComparable<T>
  4. {
  5. return a.CompareTo(b) > 0 ? a : b;
  6. }
  7. }
  8. Utilities utilities = new Utilities();
  9. int maxInt = utilities.Max(10, 20); // 20
  10. string maxString = utilities.Max("apple", "banana"); // "banana"

扩展方法

扩展方法允许向现有类型添加新的方法,而无需修改类型本身。

定义扩展方法

使用 this 关键字定义扩展方法:

  1. public static class StringExtensions
  2. {
  3. public static int WordCount(this string str)
  4. {
  5. return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
  6. }
  7. }
  8. string text = "Hello, world!";
  9. int count = text.WordCount(); // 2

动态类型

动态类型允许在运行时确定类型,而不是在编译时确定。

使用 dynamic 关键字

使用 dynamic 关键字声明动态类型:

  1. dynamic obj = 1;
  2. Console.WriteLine(obj); // 输出1
  3. obj = "Hello, world!";
  4. Console.WriteLine(obj); // 输出 "Hello, world!"
  5. obj = new { Name = "Alice", Age = 25 };
  6. Console.WriteLine(obj.Name); // 输出 "Alice"

匿名类型

匿名类型是一种用于封装一组只读属性的轻量级数据结构。

创建匿名类型

使用 new 关键字创建匿名类型:

  1. var person = new { Name = "Alice", Age = 25 };
  2. Console.WriteLine($"Name: {person.Name}, Age: {person.Age}"); // 输出 "Name: Alice, Age: 25"

异常处理机制

C# 提供了强大的异常处理机制,帮助开发者捕获和处理运行时异常。

自定义异常

创建自定义异常类:

  1. public class CustomException : Exception
  2. {
  3. public CustomException(string message) : base(message) { }
  4. }
  5. public class Program
  6. {
  7. public static void Main()
  8. {
  9. try
  10. {
  11. throw new CustomException("This is a custom exception.");
  12. }
  13. catch (CustomException ex)
  14. {
  15. Console.WriteLine(ex.Message);
  16. }
  17. }
  18. }

文件和流

C# 提供了丰富的文件和流操作类,用于处理文件输入输出操作。

文件读写

使用 File 类进行文件读写操作:

  1. string path = "example.txt";
  2. string content = "Hello, world!";
  3. // 写入文件
  4. File.WriteAllText(path, content);
  5. // 读取文件
  6. string readContent = File.ReadAllText(path);
  7. Console.WriteLine(readContent); // 输出 "Hello, world!"

使用 FileStream

使用 FileStream 进行文件读写操作:

  1. string path = "example.bin";
  2. // 写入文件
  3. using (FileStream fs = new FileStream(path, FileMode.Create))
  4. {
  5. byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  6. fs.Write(data, 0, data.Length);
  7. }
  8. // 读取文件
  9. using (FileStream fs = new FileStream(path, FileMode.Open))
  10. {
  11. byte[] data = new byte[10];
  12. fs.Read(data, 0, data.Length);
  13. foreach (byte b in data)
  14. {
  15. Console.Write(b + " "); // 输出 "0 1 2 3 4 5 6 7 8 9"
  16. }
  17. }

JSON 处理

C# 提供了丰富的库用于处理 JSON 数据,例如 Json.NET(又名 Newtonsoft.Json)库。

序列化和反序列化

使用 Json.NET 库进行 JSON 序列化和反序列化:

  1. using Newtonsoft.Json;
  2. public class Person
  3. {
  4. public string Name { get; set; }
  5. public int Age { get; set; }
  6. }
  7. Person person = new Person { Name = "Alice", Age = 25 };
  8. // 序列化
  9. string json = JsonConvert.SerializeObject(person);
  10. Console.WriteLine(json); // 输出 {"Name":"Alice","Age":25}
  11. // 反序列化
  12. Person deserializedPerson = JsonConvert.DeserializeObject<Person>(json);
  13. Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}"); // 输出 "Name: Alice, Age: 25"

正则表达式

正则表达式是一种用于模式匹配和文本处理的强大工具。

使用正则表达式

使用 Regex 类进行正则表达式操作:

  1. using System.Text.RegularExpressions;
  2. string pattern = @"\d+";
  3. string input = "There are 123 apples";
  4. // 匹配模式
  5. Match match = Regex.Match(input, pattern);
  6. if (match.Success)
  7. {
  8. Console.WriteLine($"Found match: {match.Value}"); // 输出 "Found match: 123"
  9. }
  10. // 替换模式
  11. string replaced = Regex.Replace(input, pattern, "456");
  12. Console.WriteLine(replaced); // 输出 "There are 456 apples"

日期和时间

C# 提供了丰富的日期和时间处理类,如 DateTimeTimeSpanDateTimeOffset

日期和时间操作

使用 DateTime 类进行日期和时间操作:

  1. DateTime now = DateTime.Now;
  2. Console.WriteLine($"Current date and time: {now}");
  3. DateTime future = now.AddDays(10);
  4. Console.WriteLine($"Future date and time: {future}");
  5. TimeSpan duration = future - now;
  6. Console.WriteLine($"Duration: {duration.TotalDays} days"); // 输出 "Duration: 10 days"

时间间隔

使用 TimeSpan 类表示时间间隔:

  1. TimeSpan timeSpan = new TimeSpan(1, 2, 30, 45); // 1 day, 2 hours, 30 minutes, 45 seconds
  2. Console.WriteLine($"Total hours: {timeSpan.TotalHours}"); // 输出 "Total hours: 26.5125"

反射

反射是一种在运行时获取类型信息和调用方法的机制。

使用反射获取类型信息

使用 System.Reflection 命名空间

进行反射操作:

  1. using System;
  2. using System.Reflection;
  3. public class Person
  4. {
  5. public string Name { get; set; }
  6. public int Age { get; set; }
  7. public void Introduce()
  8. {
  9. Console.WriteLine($"Hi, I'm {Name} and I'm {Age} years old.");
  10. }
  11. }
  12. Type type = typeof(Person);
  13. Console.WriteLine($"Type: {type.Name}");
  14. PropertyInfo[] properties = type.GetProperties();
  15. foreach (PropertyInfo property in properties)
  16. {
  17. Console.WriteLine($"Property: {property.Name}");
  18. }
  19. MethodInfo method = type.GetMethod("Introduce");
  20. Console.WriteLine($"Method: {method.Name}");

动态调用方法

使用反射动态调用方法:

  1. Person person = new Person { Name = "Alice", Age = 25 };
  2. MethodInfo method = typeof(Person).GetMethod("Introduce");
  3. method.Invoke(person, null); // 输出 "Hi, I'm Alice and I'm 25 years old."

异步流

异步流是一种处理异步数据流的方法,通常用于处理大量数据或需要长时间运行的操作。

定义异步流方法

使用 IAsyncEnumerable<T> 定义异步流方法:

  1. using System.Collections.Generic;
  2. using System.Threading.Tasks;
  3. public class DataGenerator
  4. {
  5. public async IAsyncEnumerable<int> GetNumbersAsync()
  6. {
  7. for (int i = 0; i < 10; i++)
  8. {
  9. await Task.Delay(500); // 模拟异步操作
  10. yield return i;
  11. }
  12. }
  13. }

消费异步流

使用 await foreach 消费异步流:

  1. DataGenerator generator = new DataGenerator();
  2. await foreach (int number in generator.GetNumbersAsync())
  3. {
  4. Console.WriteLine(number); // 输出 0 1 2 3 4 5 6 7 8 9
  5. }

预处理指令

预处理指令用于在编译过程中对代码进行条件编译或定义符号。

常见预处理指令

使用 #define#if#else#elif#endif 预处理指令:

  1. #define DEBUG
  2. using System;
  3. public class Program
  4. {
  5. public static void Main()
  6. {
  7. #if DEBUG
  8. Console.WriteLine("Debug mode");
  9. #else
  10. Console.WriteLine("Release mode");
  11. #endif
  12. }
  13. }

调试和日志

调试和日志记录是确保代码质量和诊断问题的重要手段。

使用 Debug 类

使用 System.Diagnostics.Debug 类记录调试信息:

  1. using System.Diagnostics;
  2. public class Program
  3. {
  4. public static void Main()
  5. {
  6. Debug.WriteLine("This is a debug message.");
  7. }
  8. }

使用日志库

使用 Microsoft.Extensions.Logging 库记录日志:

  1. using Microsoft.Extensions.Logging;
  2. using System;
  3. public class Program
  4. {
  5. public static void Main()
  6. {
  7. using var loggerFactory = LoggerFactory.Create(builder =>
  8. {
  9. builder.AddConsole();
  10. });
  11. ILogger logger = loggerFactory.CreateLogger<Program>();
  12. logger.LogInformation("This is an info message.");
  13. logger.LogWarning("This is a warning message.");
  14. logger.LogError("This is an error message.");
  15. }
  16. }

单元测试

单元测试是验证代码正确性的重要手段。C# 常用的单元测试框架包括 MSTest、NUnit 和 xUnit。

使用 xUnit 进行单元测试

安装 xUnit 包并编写测试代码:

  1. using Xunit;
  2. public class Calculator
  3. {
  4. public int Add(int a, int b)
  5. {
  6. return a + b;
  7. }
  8. }
  9. public class CalculatorTests
  10. {
  11. [Fact]
  12. public void Add_ReturnsSum()
  13. {
  14. Calculator calculator = new Calculator();
  15. int result = calculator.Add(5, 10);
  16. Assert.Equal(15, result);
  17. }
  18. }

结论

C# 是一种功能强大、灵活且易于学习的编程语言,广泛应用于各种领域。通过本文的详细介绍,希望读者对C#的基础语法有了全面的了解,并能在实际开发中充分利用C#的强大功能。无论是初学者还是有经验的开发者,都可以通过不断学习和实践,提升自己的编程技能,成为C#领域的专家。