C# 是一种现代、通用、面向对象的编程语言,由微软在 .NET 平台上开发。自 2000 年首次发布以来,C# 已经发展出许多高级特性,使其成为开发各种应用程序的强大工具。本文将深入探讨 C# 的一些关键高级特性,并展示如何在实际编程中有效利用它们。
1. 异步编程与 async
和 await
关键字
背景
在现代应用程序中,异步编程非常重要,尤其是在处理 I/O 操作时,如读取文件、访问网络资源或与数据库交互。同步操作可能导致应用程序的 UI 无响应,从而影响用户体验。
async
和 await
简介
C# 5.0 引入了 async
和 await
关键字,使异步编程变得更加直观和易于实现。async
标记一个方法为异步方法,而 await
则用于等待异步操作的完成。
示例代码
代码语言:javascript复制public async Task<string> GetDataFromUrlAsync(string url)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string data = await response.Content.ReadAsStringAsync();
return data;
}
}
在上述代码中,GetDataFromUrlAsync
方法被标记为异步方法,并使用 await
关键字等待 HttpClient
异步操作的完成。这种写法不仅简化了代码,还能有效避免阻塞主线程。
2. LINQ(语言集成查询)
背景
数据处理是大多数应用程序的核心部分。传统的数据处理方式通常涉及大量的循环和条件判断,而这些代码往往难以阅读和维护。
LINQ 简介
LINQ(Language Integrated Query)是 C# 中用于数据查询和操作的强大工具。它允许开发者使用查询语法直接在 C# 代码中进行数据操作,从而提高代码的可读性和可维护性。
示例代码
代码语言:javascript复制List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = from number in numbers
where number % 2 == 0
select number;
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
在上述代码中,LINQ 查询用于筛选出列表中的偶数,并通过 foreach
循环打印出来。LINQ 提供了一种声明式的方式来处理数据,使代码更加简洁和直观。
3. 泛型(Generics)
背景
在许多情况下,我们希望编写能够处理不同类型数据的通用代码。在没有泛型的情况下,这通常通过使用 object
类型和类型转换来实现,但这会带来性能开销和类型安全问题。
泛型简介
泛型允许我们定义类型参数,从而编写与类型无关的代码。这不仅提高了代码的重用性,还能在编译时提供类型检查,从而避免运行时错误。
示例代码
代码语言:javascript复制public class GenericList<T>
{
private List<T> _list = new List<T>();
public void Add(T item)
{
_list.Add(item);
}
public T Get(int index)
{
return _list[index];
}
}
在上述代码中,GenericList<T>
是一个泛型类,它可以存储任意类型的对象。通过使用类型参数 T
,我们可以创建类型安全且高效的集合类。
4. 反射(Reflection)
背景
反射是指在运行时检查和操作类型信息的能力。它在许多高级编程任务中非常有用,例如动态类型创建、序列化和依赖注入。
反射简介
C# 提供了一组强大的反射 API,使开发者能够在运行时获取类型信息、调用方法和访问属性。
示例代码
代码语言:javascript复制public void PrintProperties(object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
Console.WriteLine($"{property.Name} = {property.GetValue(obj)}");
}
}
在上述代码中,PrintProperties
方法使用反射获取对象的所有属性,并打印它们的名称和值。这展示了如何使用反射在运行时动态操作对象。
5. 表达式树(Expression Trees)
背景
表达式树是一种能够表示代码结构的树状数据结构。它在编译时生成,可以在运行时解析和执行。表达式树在动态语言实现、LINQ 提供程序和规则引擎等领域非常有用。
表达式树简介
C# 提供了一组 API,用于创建、修改和编译表达式树。通过表达式树,开发者可以在运行时生成并执行代码,从而实现高度动态化的应用程序。
示例代码
代码语言:javascript复制Expression<Func<int, int, int>> add = (a, b) => a b;
Func<int, int, int> compiledAdd = add.Compile();
int result = compiledAdd(2, 3);
Console.WriteLine(result); // 输出 5
在上述代码中,我们创建了一个表示加法操作的表达式树,并将其编译成可执行的委托。表达式树使得我们能够在运行时生成和执行代码,从而实现更灵活的编程模型。
6. 委托和事件
背景
委托是 C# 中的一种类型安全的函数指针,允许开发者将方法作为参数传递。事件是基于委托的一种特殊机制,用于在对象之间实现松散耦合的消息传递。
委托和事件简介
委托和事件使得开发者能够编写灵活的代码,实现回调和发布-订阅模式。委托定义了方法签名,而事件则允许对象订阅和响应特定的操作。
示例代码
代码语言:javascript复制public delegate void Notify(); // 定义委托
public class Process
{
public event Notify ProcessCompleted; // 定义事件
public void StartProcess()
{
Console.WriteLine("Process Started!");
// 模拟过程
Thread.Sleep(2000);
OnProcessCompleted();
}
protected virtual void OnProcessCompleted()
{
ProcessCompleted?.Invoke(); // 触发事件
}
}
public class Program
{
public static void Main(string[] args)
{
Process process = new Process();
process.ProcessCompleted = Process_ProcessCompleted; // 订阅事件
process.StartProcess();
}
private static void Process_ProcessCompleted()
{
Console.WriteLine("Process Completed!");
}
}
在上述代码中,我们定义了一个 Notify
委托和一个 ProcessCompleted
事件。在 Process
类中,当进程完成时触发 ProcessCompleted
事件,并在 Program
类中订阅该事件,从而实现事件驱动的编程模型。
7. 属性(Properties)
背景
属性是 C# 中的一种特殊成员,提供了对字段的受控访问。它们使得我们能够在访问字段时添加额外的逻辑,例如验证和变更通知。
属性简介
属性使用 get
和 set
访问器定义,分别用于读取和写入属性值。属性的语法类似于字段,但提供了更多的控制和灵活性。
示例代码
代码语言:javascript复制public class Person
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Name cannot be null or empty");
_name = value;
}
}
}
在上述代码中,Name
属性通过 get
访问器和 set
访问器提供对 _name
字段的受控访问。set
访问器中包含验证逻辑,以确保名称不为空。
8. 可空类型(Nullable Types)
背景
在实际开发中,我们经常需要处理可能为空的值。传统的值类型(如 int
、double
)不能直接表示空值,导致我们不得不使用额外的标志位或特殊值来处理这种情况。
可空类型简介
C# 提供了可空类型(Nullable Types),使得值类型能够表示空值。可空类型使用 Nullable<T>
结构或简写形式 T?
表示。
示例代码
代码语言:javascript复制int? optionalValue = null;
if (optionalValue.HasValue)
{
Console.WriteLine($"Value: {optionalValue.Value}");
}
else
{
Console.WriteLine("Value is null");
}
在上述代码中,optionalValue
是一个可空的 int
类型,可以存储 null
。通过 HasValue
属性,我们可以检查可空