简介:C#是一种现代面向对象编程语言,广泛应用于多个领域。本资源集包含30个C#小程序示例,涵盖基础语法、数据类型、函数方法、类与对象、集合与数组、异常处理、LINQ查询、文件操作、多线程、事件与委托、GUI编程、.NET框架和异步编程等内容。这些示例旨在为初学者和经验丰富的开发者提供实践机会,帮助他们掌握C#编程基础和解决实际问题的能力。
1. C#基础语法与数据类型
在编程的世界中,C#(C Sharp)以其优雅的语法和强大的功能,成为了开发者的优选语言之一。本章,我们将从基础开始,探讨C#的关键语法和数据类型。
首先,我们要熟悉C#中的关键字,如 class
、 namespace
、 if
等,它们是构成C#程序的基本元素。然后,我们将探讨如何使用标识符来命名变量、方法和类。标识符的选择不仅要遵循C#的命名规则,更需要有助于代码的可读性和可维护性。
接下来,数据类型作为编程的核心,我们将详细介绍C#中的数值类型、布尔类型、字符类型和字符串类型。我们会讨论它们的特性和使用场景,并演示如何声明和初始化变量。
此外,本章还包括了如何在C#程序中使用数组和集合,以及如何高效地操作这些数据结构。你将学习到数组的声明、初始化和遍历,以及集合类如List和Dictionary的使用方法。
通过本章的学习,你将掌握C#编程的基础知识,为深入学习后续章节打下坚实的基础。
// 示例代码:变量声明和使用
int number = 42; // 声明并初始化一个整型变量
string name = "C# Programming"; // 声明并初始化一个字符串变量
bool isFun = true; // 声明并初始化一个布尔型变量
// 输出变量值
Console.WriteLine($"The number is {number}.");
Console.WriteLine($"Is programming fun? {isFun}.");
Console.WriteLine($"I love {name}!");
在上述代码中,我们声明了三种不同类型的变量,并使用 Console.WriteLine
方法输出了它们的值。这只是C#语法和数据类型的一个非常简单的入门示例。随着章节的深入,我们将逐步揭开C#的更多精彩特性。
2. C#函数与方法示例
函数和方法概念
在C#中,函数(Function)和方法(Method)通常可以互换使用。它们代表程序中的一段代码块,可以执行特定的任务,并且可以有输入参数和返回值。函数是面向过程的编程的一部分,而方法则更多地与面向对象编程联系在一起。在C#中,所有的函数都是类的一部分,因此更准确地说是方法。
定义方法
方法的定义从返回类型开始,然后是方法名、括号内的参数列表(如果有的话),最后是方法体。下面是一个简单方法的定义示例:
public int Add(int a, int b) // 返回类型为int,方法名为Add,有两个int类型的参数
{
return a + b; // 返回两个参数的和
}
在这个例子中, Add
方法有返回类型 int
,这意味着它会返回一个整数值。方法名 Add
后跟着一对括号,括号中定义了两个名为 a
和 b
的整数类型参数。方法体包含了实现加法逻辑的单个语句。
参数传递
C#支持值类型和引用类型参数传递。值类型参数传递时,传递的是实际值的副本;而引用类型参数传递时,传递的是实际对象的引用副本,意味着对引用类型参数的修改会影响原始对象。
返回值
方法可以通过 return
语句返回值。如果方法的返回类型不是 void
,则必须包含返回语句来返回特定类型的值。在上面的 Add
方法中,我们返回了两个参数的和。
方法重载
方法重载是指在同一个类中可以存在多个同名方法,只要它们的参数类型或参数数量不同即可。方法重载为方法的使用提供了灵活性,允许根据不同的参数类型或数量调用相同名称的方法。
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
在上述代码中, Add
方法被重载了三次,每次有不同的参数类型或数量。根据传递给 Add
方法的参数类型和数量,编译器会决定调用哪个重载版本。
方法使用实例
下面是一个使用 Add
方法的实例:
int sum = Add(5, 10); // 调用第一个Add方法,sum将会是15
double doubleSum = Add(2.5, 3.7); // 调用第二个Add方法,doubleSum将会是6.2
int sum3 = Add(1, 2, 3); // 调用第三个Add方法,sum3将会是6
方法的高级特性
在C#中,方法还可以有可选参数和命名参数,这为方法调用提供了更多的灵活性。
可选参数
可以给方法参数提供默认值,调用方法时如果没有传递该参数,则会使用默认值。
public void DisplayMessage(string message, bool includeTime = true)
{
if (includeTime)
Console.WriteLine($"{DateTime.Now}: {message}");
else
Console.WriteLine(message);
}
// 调用
DisplayMessage("Hello World!"); // 不传递includeTime参数,默认为true
DisplayMessage("Hello World!", false); // 明确传递includeTime参数为false
命名参数
在调用方法时,可以使用参数名来指定值,而不需要考虑参数的顺序。
DisplayMessage(message: "Hi, what's up?", includeTime: false);
小结
本章节通过详细的说明和实例展示了C#中方法的定义和使用,包括参数传递、返回值、方法重载以及方法的高级特性如可选参数和命名参数。通过这些基础知识,可以更有效地利用方法来构建复杂的C#程序。
3. 面向对象编程:类与对象
类与对象概念
面向对象编程(OOP)是C#编程语言中的核心概念。类是OOP的基础,它是构建对象的蓝图或模板。对象是类的实例,拥有类的属性和方法。理解类与对象是构建复杂系统和应用不可或缺的一步。
类的定义与实例化
在C#中,类使用关键字 class
进行定义。它包括属性、方法、字段和事件等成员。
public class Person
{
// 属性
public string Name { get; set; }
public int Age { get; set; }
// 方法
public void SayHello()
{
Console.WriteLine("Hello, my name is " + Name);
}
// 构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
-
Name
和Age
是公共属性,可以被外部访问和修改。 -
SayHello
是一个方法,实现了一个行为。 -
Person(string name, int age)
是一个构造函数,用于创建对象时初始化。
对象的创建和使用
在创建类的对象时,我们通常使用 new
关键字:
Person person = new Person("Alice", 30);
person.SayHello();
-
person
是Person
类的一个实例。 - 使用
person.SayHello();
调用实例方法,执行打印操作。
类的继承
继承是面向对象编程中的一个关键特性。它允许创建类的层次结构,其中派生类继承基类的字段和方法。
public class Employee : Person
{
public string Department { get; set; }
public Employee(string name, int age, string department) : base(name, age)
{
Department = department;
}
public void SayDepartment()
{
Console.WriteLine("I work in the " + Department);
}
}
-
Employee
类继承了Person
类的所有成员,并添加了Department
属性和SayDepartment
方法。
多态
多态允许我们使用基类类型的引用来指向派生类的对象,并调用合适的方法实现。在C#中,多态是通过虚方法和重写实现的。
public class Vehicle
{
public virtual void Start()
{
Console.WriteLine("Vehicle is starting");
}
}
public class Car : Vehicle
{
public override void Start()
{
Console.WriteLine("Car is starting");
}
}
// 使用
Vehicle vehicle = new Car();
vehicle.Start(); // 输出: Car is starting
-
Car
类重写了Vehicle
类的Start
方法。
封装
封装是隐藏对象内部的复杂性,仅暴露接口的方法。在C#中,通过使用访问修饰符实现封装。
public class BankAccount
{
private decimal balance;
public decimal GetBalance()
{
return balance;
}
public void Deposit(decimal amount)
{
if (amount > 0)
{
balance += amount;
}
}
}
-
balance
是私有字段,只能在类内部访问。 -
GetBalance
和Deposit
是公开方法,外部通过这些方法操作私有字段。
类与对象的高级应用
静态成员
静态成员属于类本身,而非类的任何实例。静态字段和静态方法通常用于创建不需要实例化类的对象即可使用的功能。
public class Utility
{
public static void Log(string message)
{
Console.WriteLine("Log: " + message);
}
}
// 使用
Utility.Log("Application started");
-
Log
方法是静态的,可以不需要Utility
类的实例即可调用。
抽象类与接口
抽象类和接口是实现抽象级别的OOP概念。抽象类使用 abstract
关键字定义,不能实例化。它可以包含抽象方法和非抽象方法。
public abstract class Shape
{
public abstract void Draw();
}
接口使用 interface
关键字定义,它声明了类必须实现的方法,但不提供方法实现。
public interface IRenderer
{
void Render();
}
对象的比较
对象比较可以是值比较或引用比较。值比较通常比较对象的内容,而引用比较比较对象的内存地址。
public static bool AreEqual(Person p1, Person p2)
{
if (ReferenceEquals(p1, p2)) return true;
if (p1 is null || p2 is null) return false;
return p1.Name == p2.Name && p1.Age == p2.Age;
}
-
ReferenceEquals
用于引用比较。 -
Equals
方法和运算符==
可以被重载以支持值比较。
对象的序列化
对象的序列化是将对象的状态保存到存储介质中,或者通过网络传输的过程。C#提供了 System.Runtime.Serialization
命名空间,它支持对象的序列化和反序列化。
[Serializable]
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
// 序列化示例
User user = new User { Name = "John", Age = 30 };
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("user.data", FileMode.Create);
formatter.Serialize(stream, user);
stream.Close();
-
[Serializable]
属性标记允许对象被序列化。 - 使用
BinaryFormatter
类对User
对象进行序列化操作。
总结
类和对象是构成面向对象编程的基础。本章深入探讨了类的定义、对象的创建、继承、多态、封装等核心概念,并通过实例展示了如何利用这些特性来实现复杂的功能和解决方案。通过静态成员、抽象类、接口以及对象的比较和序列化等高级技术,C#程序员可以编写出结构清晰、易于维护和扩展的应用程序。下一章将详细介绍C#中的集合与数组应用,探索更多数据管理的高效工具和方法。
4. C#集合与数组应用
4.1 集合类型简介
在C#中,集合是存储一组元素的容器,这些元素可以是任意数据类型。集合是编程中常用的数据结构,用于管理数据集合。C# 提供了丰富的集合类,主要分为接口和类两大类。接口集合如 IEnumerable<T>
、 ICollection<T>
、 IList<T>
等,而类集合如 List<T>
、 Dictionary<TKey, TValue>
、 HashSet<T>
等。
4.1.1 列表(List)
列表是最常用的集合类型之一。 List<T>
类是一个泛型集合,允许动态地添加和移除元素,它实现了 IList<T>
接口。列表元素可以通过索引直接访问。
使用列表
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
numbers.Add(6); // 添加元素到列表末尾
numbers.Insert(2, 25); // 在指定索引处插入元素
numbers.RemoveAt(0); // 移除指定索引处的元素
Console.WriteLine(numbers.Contains(25)); // 检查列表是否包含指定元素
foreach (int number in numbers)
{
Console.WriteLine(number);
}
}
}
4.1.2 字典(Dictionary)
字典是一种存储键值对的数据结构。在C#中, Dictionary<TKey, TValue>
类实现了 IDictionary<TKey, TValue>
接口。字典提供了通过键快速访问值的能力。
使用字典
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary<string, int> ages = new Dictionary<string, int>
{
{"Alice", 24},
{"Bob", 27}
};
ages.Add("Charlie", 32); // 添加新的键值对
if (ages.TryGetValue("Alice", out int aliceAge))
{
Console.WriteLine($"Alice's age is {aliceAge}.");
}
foreach (KeyValuePair<string, int> pair in ages)
{
Console.WriteLine($"{pair.Key} is {pair.Value} years old.");
}
}
}
4.1.3 队列(Queue)
队列是一种先进先出(FIFO)的数据结构。 Queue<T>
类实现了 IEnumerable<T>
、 ICollection
和 ISerializable
接口。
使用队列
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Queue<string> queue = new Queue<string>();
queue.Enqueue("First"); // 入队操作
queue.Enqueue("Second");
queue.Enqueue("Third");
Console.WriteLine(queue.Dequeue()); // 出队操作,移除并返回队列的前端元素
foreach (string s in queue)
{
Console.WriteLine($"Queue: {s}");
}
}
}
4.2 数组
数组是一种数据结构,用于存储固定大小且类型相同的元素序列。在C#中,数组是引用类型,可以通过索引访问数组元素。
4.2.1 声明和初始化数组
数组的声明包括指定数组元素的类型和数组变量的名字,以及一对方括号来表示数组的维度。
string[] names = new string[3]; // 声明一个包含3个字符串的数组
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Charlie";
4.2.2 多维数组
多维数组用于存储多维度的数据结构。二维数组是最常见的多维数组类型。
int[,] matrix = new int[3, 2];
matrix[0, 0] = 1;
matrix[0, 1] = 2;
matrix[1, 0] = 3;
matrix[1, 1] = 4;
matrix[2, 0] = 5;
matrix[2, 1] = 6;
4.3 集合与数组的高级操作
4.3.1 LINQ查询
LINQ(语言集成查询)提供了一种查询数据源(包括数组和集合)的简洁方法。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = from number in numbers
where number % 2 == 0
select number;
foreach (int number in evenNumbers)
{
Console.WriteLine(number);
}
}
}
4.3.2 集合的排序和过滤
排序和过滤是处理数据时常见的操作。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 5, 2, 9, 1, 6 };
var sortedNumbers = numbers.OrderBy(n => n); // 排序
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList(); // 过滤偶数
foreach (int number in sortedNumbers)
{
Console.WriteLine(number);
}
foreach (int number in evenNumbers)
{
Console.WriteLine(number);
}
}
}
4.4 性能优化与最佳实践
在使用集合与数组时,性能优化和最佳实践对提高效率和可读性至关重要。
4.4.1 使用泛型集合
使用泛型集合可以提高代码的类型安全性和性能,因为泛型减少了不必要的装箱和拆箱操作。
4.4.2 避免不必要的集合转换
频繁地转换集合类型可能会导致性能下降。应尽量在创建集合时就选择合适的类型。
4.4.3 利用集合的内置方法
大多数集合类提供了丰富的方法来操作数据,例如 AddRange
、 RemoveAll
、 FindAll
等。使用这些内置方法可以提高代码的可读性和效率。
在本章中,我们详细了解了C#中的集合与数组,包括它们的定义、使用方式和性能优化的策略。通过实例演示了如何高效处理和操作数据集合,这些知识对于构建性能优良的应用程序至关重要。随着对集合与数组的深入理解和实践,您将能够灵活应对数据管理的各种挑战。
5. 异常处理机制
异常处理是确保程序稳定运行和良好用户体验的关键技术。C#中的异常处理机制能够帮助开发者捕捉程序运行时的错误情况,防止程序因未处理的错误而意外终止。在这一章节中,我们将深入了解C#异常处理的细节,并通过实际代码示例来演示如何在日常开发中应用这些技术。
5.1 基本的异常处理结构
C#中的异常处理是通过使用 try
、 catch
、 finally
和 throw
关键字来实现的。 try
块包含了可能抛出异常的代码。如果在 try
块中的代码执行过程中发生异常,那么异常会被传递到最近的 catch
块中。 finally
块则无论是否发生异常都会执行,通常用于资源的清理和释放。
下面是一个简单的异常处理结构示例:
try
{
// 尝试执行的代码
int[] numbers = new int[5];
Console.WriteLine(numbers[10]); // 这里会抛出一个IndexOutOfRangeException
}
catch (Exception ex)
{
// 捕获异常并进行处理
Console.WriteLine("发生了一个错误: " + ex.Message);
}
finally
{
// 最终块,无论是否发生异常都会执行
Console.WriteLine("这段代码总是会执行。");
}
5.2 自定义异常
在某些情况下,我们需要定义自己的异常类型,以便提供更具体的错误信息。通过继承 Exception
类来创建自定义异常,可以让异常处理过程更加具有可读性和维护性。
// 自定义异常类
public class CustomException : Exception
{
public CustomException(string message) : base(message) {}
}
// 使用自定义异常
try
{
throw new CustomException("这是一个自定义异常。");
}
catch (CustomException ex)
{
Console.WriteLine("捕获到自定义异常: " + ex.Message);
}
5.3 异常的抛出和捕获
异常的抛出是通过 throw
关键字实现的。在C#中,可以抛出任何继承自 System.Exception
的类型。有时候,我们可能需要根据特定条件抛出异常,这时可以在 catch
块内部使用 throw
来重新抛出异常。
// 使用条件语句抛出异常
try
{
int result = Calculate(10, 0); // Calculate函数可能会抛出异常
}
catch (DivideByZeroException ex)
{
Console.WriteLine("捕获到除零异常。");
throw; // 重新抛出当前捕获的异常
}
// 计算函数
public int Calculate(int x, int y)
{
if (y == 0)
throw new DivideByZeroException();
return x / y;
}
5.4 处理多个异常
当一段代码有可能抛出不同类型的多个异常时,可以使用多个 catch
块来分别处理。需要注意的是, catch
块的顺序很重要,因为C#会从上至下检查 catch
块,一旦某个块匹配,其余的将不会被执行。
try
{
// 可能抛出不同异常的代码
int result = int.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine("输入格式不正确。");
}
catch (OverflowException ex)
{
Console.WriteLine("数字超出范围。");
}
catch (Exception ex)
{
// 默认处理所有其他异常
Console.WriteLine("发生了一个未知错误。");
}
在上述的五个示例中,我们介绍了C#异常处理的不同方面,包括基本的 try-catch-finally
结构,自定义异常的创建与使用,异常的抛出与捕获,以及处理多个异常的情况。理解这些概念对于编写健壮和可靠的C#应用程序至关重要。
在实践中,合理使用异常处理可以提高程序的可维护性和用户的体验。例如,在处理用户输入时,适当的异常处理可以保证程序不会因为用户的错误操作而崩溃,并且能够给出友好的错误信息。在下一节中,我们将探讨如何利用LINQ技术进行高效的数据查询操作。
简介:C#是一种现代面向对象编程语言,广泛应用于多个领域。本资源集包含30个C#小程序示例,涵盖基础语法、数据类型、函数方法、类与对象、集合与数组、异常处理、LINQ查询、文件操作、多线程、事件与委托、GUI编程、.NET框架和异步编程等内容。这些示例旨在为初学者和经验丰富的开发者提供实践机会,帮助他们掌握C#编程基础和解决实际问题的能力。