C# 泛型
一、泛型概述
在 C# 中,泛型(Generics)是一种强大的编程特性,它允许您编写可以在不同数据类型上工作的通用代码,而无需为每种数据类型编写不同的代码。通过泛型,您可以编写更灵活、可复用的代码,并提高代码的类型安全性和性能。
二、泛型特性
(一) 三大特性
1、类型安全性(Type Safety): 泛型提供了更强大的类型检查机制,使得编译器能够在编译时检查代码,防止不同数据类型之间的混淆。通过泛型,开发者可以在编译时发现并修复类型错误,而不是在运行时。
// 非泛型的 List,容易引起类型混淆 ArrayList nonGenericList = new ArrayList(); nonGenericList.Add("Hello"); int length = ((string)nonGenericList[0]).Length; // 运行时可能抛出异常 // 泛型的 List,类型安全 List<string> genericList = new List<string>(); genericList.Add("Hello"); int lengthSafe = genericList[0].Length; // 编译时就能发现错误
2、代码重用(Code Reusability): 泛型允许编写一次通用的代码,然后在不同的数据类型上重复使用,而无需为每种数据类型都编写特定的代码。这有助于减少代码的冗余,提高代码的可维护性和可读性。
// 非泛型的 Swap 方法,需要为每种类型写不同的实现 void SwapInt(ref int a, ref int b) { int temp = a; a = b; b = temp; } void SwapString(ref string a, ref string b) { string temp = a; a = b; b = temp; } // 泛型的 Swap 方法,通用于不同的数据类型 void Swap<T>(ref T a, ref T b) { T temp = a; a = b; b = temp; }
3、性能优化(Performance Optimization): 泛型在一些情况下可以提高代码的性能。由于泛型是在编译时生成特定类型的代码,避免了运行时的装箱和拆箱操作。这有助于提高程序的执行效率。
// 非泛型的集合,需要装箱和拆箱操作 ArrayList nonGenericList = new ArrayList(); nonGenericList.Add(42); int value = (int)nonGenericList[0]; // 运行时装箱和拆箱 // 泛型的集合,避免了装箱和拆箱 List<int> genericList = new List<int>(); genericList.Add(42); int valueNoBoxing = genericList[0]; // 没有装箱和拆箱
这三大特性使得泛型成为现代编程语言中的一个重要工具,为开发者提供了更灵活、更安全、更高效的编程方式。
(二) 其他优点
- 更好的抽象: 泛型允许你以一种更抽象的方式表示算法和数据结构,而不是专门针对特定类型。这使得代码更易理解、更容易维护,并且更容易适应变化。
- 灵活性: 泛型提供了更大的灵活性,允许开发人员以一种通用的方式处理各种数据类型,而不必为每种类型编写特定的代码。
- 可读性: 使用泛型可以使代码更加清晰和简洁,因为它减少了重复的代码块。这使得代码更易读懂,也更容易维护。
- 容错性: 泛型能够提供更好的容错性,因为它在编译时就能够捕获到类型错误。这有助于避免在运行时发生的意外错误。
三、类型参数(Type Parameters)
在定义泛型类型或方法时,您可以声明一个或多个类型参数,它们在使用时将被实际类型替换。类型参数用尖括号(<>)括起来,并放置在类型或方法名称之后。
public class MyGenericClass<T> { public T MyProperty { get; set; } public void MyMethod(T value) { // Do something with value } }
四、泛型类(Generic Classes)
这些是具有一个或多个类型参数的类。您可以实例化泛型类,为每个类型参数提供实际类型。
MyGenericClass<int> intInstance = new MyGenericClass<int>(); MyGenericClass<string> stringInstance = new MyGenericClass<string>();
五、泛型方法(Generic Methods)
这些是具有类型参数的方法,可以在调用时为这些参数提供实际类型。泛型方法可以是类的成员方法,也可以是静态方法。
public T MyGenericMethod<T>(T value) { return value; }
六、泛型约束(Generic Constraints)
有时希望对泛型参数施加某些约束,以确保这些参数满足特定条件。例如,可以要求泛型类型实现特定接口或具有默认构造函数。
public class MyGenericClass<T> where T : IComparable { // Class body }
七、泛型接口(Generic Interfaces)
这些是具有一个或多个类型参数的接口。泛型接口可以被泛型类实现。
public interface IMyInterface<T> { void MyMethod(T value); }
八、泛型结构体
这些是具有一个或多个类型参数的结构体。可以实例化泛型结构体,为每个类型参数提供实际类型。
public struct MyGenericStruct<T> { public T Value { get; } public MyGenericStruct(T value) { Value = value; } }
九、List<T>
List<T> 是 C# 中的一个动态数组实现,它属于 System.Collections.Generic 命名空间,用于存储和操作元素的动态集合。以下是 List<T> 的简化版本源码,用于说明其基本结构和主要成员:
namespace System.Collections.Generic { public class List<T> : IList<T> { private T[] items; // 存储元素的数组 private int count; // 实际元素的数量 public List() { items = new T[4]; // 默认初始容量为4 } public int Count => count; public void Add(T item) { EnsureCapacity(); // 确保容量足够 items[count++] = item; } public T this[int index] { get { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException(nameof(index)); return items[index]; } set { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException(nameof(index)); items[index] = value; } } private void EnsureCapacity() { if (count == items.Length) { int newCapacity = items.Length * 2; // 扩容为当前容量的两倍 T[] newItems = new T[newCapacity]; Array.Copy(items, newItems, count); items = newItems; } } // 其他 IList<T> 成员的实现... // 迭代器,用于遍历集合 public IEnumerator<T> GetEnumerator() { for (int i = 0; i < count; i++) { yield return items[i]; } } } }
(一) List.Where
- 延迟执行:Where() 方法采用延迟执行的机制,意味着它不会立即对列表进行遍历和筛选操作,而是在需要时才会执行实际的查询。
- 使用委托和Lambda表达式:Where() 方法接受一个 Func<T, bool> 委托作为参数,该委托描述了筛选条件,通常使用 Lambda 表达式表示。
- 高效实现:对于内存中的 List<T>,LINQ 提供程序会尽可能地采用高效的实现方式来执行查询操作,这可能包括使用索引、缓存或其他数据结构,而不仅仅是简单地使用 foreach 循环逐一比较。
- 根据上下文和数据源类型选择执行方式:具体的执行方式取决于上下文和数据源的类型。对于内存中的列表,可能会采用不同于其他数据源(如数据库)的执行方式。
(二) List.Max
List.Max() 方法用于获取列表中的最大值。其对性能的影响取决于列表的大小和数据类型。
- 小型列表:对于包含少量元素的小型列表,List.Max() 方法的性能影响通常可以忽略不计。即使在较小的列表中,它也需要遍历列表一次以找到最大值,但由于列表规模较小,因此性能影响很小。
- 大型列表:对于大型列表,List.Max() 方法的性能影响可能会更加显著。在这种情况下,遍历整个列表以找到最大值可能会引起性能问题,特别是在高频率调用或在性能敏感的场景中。
- 数据类型:List.Max() 方法对于基本数据类型(如整数、浮点数)的性能影响通常较小。但是,如果列表中的元素是自定义的复杂对象,那么计算最大值可能会涉及到比较复杂的逻辑,这可能会增加性能开销。
- 缓存:如果列表中的元素已经按照某种方式排序,或者最大值已经被计算并缓存,那么再次调用 List.Max() 方法时可能会更快,因为不需要重新遍历整个列表。
因此,虽然 List.Max() 方法本身在常规情况下的性能影响可能较小,但在处理大型列表或自定义数据类型时,需要谨慎考虑其使用频率以及可能的性能影响。在某些情况下,可能需要采取优化措施,例如使用更高效的数据结构或缓存结果,以降低性能开销。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?