摘要:本文围绕C#中的集合与泛型展开,详细介绍了
List
、Dictionary
、HashSet
等常用集合类型,深入探讨了泛型约束、协变逆变的概念与应用,分析了IEnumerable
与IQueryable
的区别与联系,还介绍了集合初始化器的使用方法。通过丰富的实操流程和完整代码示例,帮助读者全面理解和掌握C#中集合与泛型的高级特性,提升编程能力。
文章目录
【C#基础:第四部分 高级特性】集合与泛型
关键词
C#;集合;泛型;List;Dictionary;HashSet;泛型约束;协变逆变;IEnumerable;IQueryable;集合初始化器
一、引言
在C#编程中,集合与泛型是非常重要的高级特性。集合提供了一种方便的方式来存储和管理一组对象,而泛型则允许我们编写类型安全、可重用的代码。合理运用集合与泛型可以提高代码的性能、可读性和可维护性。本文将对C#中的集合与泛型进行深入探讨,通过实际案例和代码示例帮助读者更好地理解和应用这些特性。
二、List/Dictionary/HashSet
2.1 List
2.1.1 基本概念
List<T>
是一个动态数组,它可以根据需要自动调整大小。T
是泛型类型参数,表示列表中元素的类型。List<T>
提供了丰富的方法来操作列表元素,如添加、删除、查找等。
2.1.2 代码示例
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 创建一个存储整数的 List
List<int> numbers = new List<int>();
// 添加元素
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
// 访问元素
Console.WriteLine("列表中的第一个元素: " + numbers[0]);
// 遍历列表
Console.WriteLine("列表中的所有元素:");
foreach (int num in numbers)
{
Console.WriteLine(num);
}
// 删除元素
numbers.Remove(2);
Console.WriteLine("删除元素 2 后,列表中的所有元素:");
foreach (int num in numbers)
{
Console.WriteLine(num);
}
}
}
2.1.3 代码解释
- 创建
List<int>
对象numbers
,用于存储整数。 - 使用
Add
方法向列表中添加元素。 - 通过索引访问列表中的元素,如
numbers[0]
。 - 使用
foreach
循环遍历列表中的所有元素。 - 使用
Remove
方法删除指定元素。
2.1.4 实操流程
- 打开 Visual Studio,创建一个新的 C# 控制台应用程序项目。
- 将上述代码复制到
Program.cs
文件的Main
方法中。 - 按下 F5 键运行程序,观察控制台输出结果。
2.2 Dictionary<TKey, TValue>
2.2.1 基本概念
Dictionary<TKey, TValue>
是一个键值对集合,其中每个键都是唯一的。TKey
表示键的类型,TValue
表示值的类型。Dictionary<TKey, TValue>
提供了快速的查找功能,通过键可以快速定位到对应的值。
2.2.2 代码示例
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 创建一个存储字符串键和整数值的 Dictionary
Dictionary<string, int> scores = new Dictionary<string, int>();
// 添加键值对
scores.Add("Alice", 90);
scores.Add("Bob", 85);
scores.Add("Charlie", 78);
// 访问元素
Console.WriteLine("Alice 的分数: " + scores["Alice"]);
// 遍历字典
Console.WriteLine("所有学生的分数:");
foreach (KeyValuePair<string, int> pair in scores)
{
Console.WriteLine(pair.Key + ": " + pair.Value);
}
// 删除键值对
scores.Remove("Bob");
Console.WriteLine("删除 Bob 的分数后,所有学生的分数:");
foreach (KeyValuePair<string, int> pair in scores)
{
Console.WriteLine(pair.Key + ": " + pair.Value);
}
}
}
2.2.3 代码解释
- 创建
Dictionary<string, int>
对象scores
,用于存储学生姓名和分数的键值对。 - 使用
Add
方法向字典中添加键值对。 - 通过键访问字典中的值,如
scores["Alice"]
。 - 使用
foreach
循环遍历字典中的所有键值对。 - 使用
Remove
方法删除指定键的键值对。
2.2.4 实操流程
- 打开 Visual Studio,创建一个新的 C# 控制台应用程序项目。
- 将上述代码复制到
Program.cs
文件的Main
方法中。 - 按下 F5 键运行程序,观察控制台输出结果。
2.3 HashSet
2.3.1 基本概念
HashSet<T>
是一个无序的集合,其中每个元素都是唯一的。T
是泛型类型参数,表示集合中元素的类型。HashSet<T>
提供了高效的查找和插入操作,其内部使用哈希表来实现。
2.3.2 代码示例
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 创建一个存储字符串的 HashSet
HashSet<string> fruits = new HashSet<string>();
// 添加元素
fruits.Add("Apple");
fruits.Add("Banana");
fruits.Add("Apple"); // 重复元素不会被添加
// 检查元素是否存在
bool containsApple = fruits.Contains("Apple");
Console.WriteLine("集合中是否包含 Apple: " + containsApple);
// 遍历集合
Console.WriteLine("集合中的所有元素:");
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
// 删除元素
fruits.Remove("Banana");
Console.WriteLine("删除 Banana 后,集合中的所有元素:");
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
}
}
2.3.3 代码解释
- 创建
HashSet<string>
对象fruits
,用于存储水果名称。 - 使用
Add
方法向集合中添加元素,重复元素不会被添加。 - 使用
Contains
方法检查集合中是否包含指定元素。 - 使用
foreach
循环遍历集合中的所有元素。 - 使用
Remove
方法删除指定元素。
2.3.4 实操流程
- 打开 Visual Studio,创建一个新的 C# 控制台应用程序项目。
- 将上述代码复制到
Program.cs
文件的Main
方法中。 - 按下 F5 键运行程序,观察控制台输出结果。
三、泛型约束与协变逆变
3.1 泛型约束
3.1.1 基本概念
泛型约束用于限制泛型类型参数的类型范围,确保泛型类型参数满足特定的条件。常见的泛型约束有以下几种:
where T : struct
:T
必须是值类型。where T : class
:T
必须是引用类型。where T : new()
:T
必须有一个无参数的构造函数。where T : baseClass
:T
必须是baseClass
或其派生类。where T : interface
:T
必须实现指定的接口。
3.1.2 代码示例
using System;
// 定义一个泛型类,使用泛型约束
class GenericClass<T> where T : IComparable<T>
{
private T value;
public GenericClass(T value)
{
this.value = value;
}
public bool IsGreaterThan(T other)
{
return value.CompareTo(other) > 0;
}
}
class Program
{
static void Main()
{
GenericClass<int> intObj = new GenericClass<int>(10);
bool result = intObj.IsGreaterThan(5);
Console.WriteLine("10 是否大于 5: " + result);
}
}
3.1.3 代码解释
- 定义了一个泛型类
GenericClass<T>
,使用where T : IComparable<T>
约束,确保T
类型实现了IComparable<T>
接口。 - 在
GenericClass<T>
类中,定义了一个IsGreaterThan
方法,用于比较两个T
类型的对象。 - 在
Main
方法中,创建了一个GenericClass<int>
对象,并调用IsGreaterThan
方法进行比较。
3.1.4 实操流程
- 打开 Visual Studio,创建一个新的 C# 控制台应用程序项目。
- 将上述代码复制到
Program.cs
文件中。 - 按下 F5 键运行程序,观察控制台输出结果。
3.2 协变逆变
3.2.1 基本概念
协变和逆变是泛型接口和委托中的概念,用于处理泛型类型参数的转换。
- 协变(Covariance):允许将一个派生类型的泛型对象赋值给一个基类型的泛型对象。协变使用
out
关键字修饰泛型类型参数。 - 逆变(Contravariance):允许将一个基类型的泛型对象赋值给一个派生类型的泛型对象。逆变使用
in
关键字修饰泛型类型参数。
3.2.2 代码示例
using System;
// 定义一个协变接口
interface IAnimal<out T>
{
T GetAnimal();
}
// 定义一个动物类
class Animal {
}
// 定义一个狗类,继承自动物类
class Dog : Animal {
}
// 实现协变接口
class DogProvider : IAnimal<Dog>
{
public Dog GetAnimal()
{
return new Dog();
}
}
class Program
{
static void Main()
{
IAnimal<Dog> dogProvider = new DogProvider();
IAnimal<Animal> animalProvider = dogProvider; // 协变转换
Animal animal = animalProvider.GetAnimal();
Console.WriteLine("获取到的动物类型: " + animal.GetType().Name);
}
}
3.2.3 代码解释
- 定义了一个协变接口
IAnimal<out T>
,使用out
关键字修饰泛型类型参数T
。 - 定义了
Animal
类和Dog
类,Dog
类继承自Animal
类。 - 实现了
IAnimal<Dog>
接口的DogProvider
类。 - 在
Main
方法中,将IAnimal<Dog>
类型的对象赋值给IAnimal<Animal>
类型的对象,实现了协变转换。
3.2.4 实操流程
- 打开 Visual Studio,创建一个新的 C# 控制台应用程序项目。
- 将上述代码复制到
Program.cs
文件中。 - 按下 F5 键运行程序,观察控制台输出结果。
四、IEnumerable与IQueryable
4.1 IEnumerable
4.1.1 基本概念
IEnumerable<T>
是一个泛型接口,它定义了一个方法 GetEnumerator()
,用于返回一个实现了 IEnumerator<T>
接口的枚举器对象。IEnumerable<T>
是 C# 中集合的基础接口,几乎所有的集合类型都实现了该接口。
4.1.2 代码示例
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> {
1, 2, 3, 4, 5 };
// 使用 IEnumerable<T> 进行遍历
IEnumerable<int> enumerableNumbers = numbers;
foreach (int num in enumerableNumbers)
{
Console.WriteLine(num);
}
}
}
4.1.3 代码解释
- 创建一个
List<int>
对象numbers
,并初始化一些元素。 - 将
List<int>
对象赋值给IEnumerable<int>
类型的变量enumerableNumbers
。 - 使用
foreach
循环遍历enumerableNumbers
,实际上是调用了GetEnumerator()
方法获取枚举器对象进行遍历。
4.1.4 实操流程
- 打开 Visual Studio,创建一个新的 C# 控制台应用程序项目。
- 将上述代码复制到
Program.cs
文件的Main
方法中。 - 按下 F5 键运行程序,观察控制台输出结果。
4.2 IQueryable
4.2.1 基本概念
IQueryable<T>
是 IEnumerable<T>
的子接口,它提供了对数据的延迟查询功能。IQueryable<T>
通常用于与数据库等数据源进行交互,它会将查询表达式转换为相应的数据源查询语句,然后在需要时执行查询。
4.2.2 代码示例
using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
public string Name {
get; set; }
public int Age {
get; set; }
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person {
Name = "Alice", Age = 25 },
new Person {
Name = "Bob", Age = 30 },
new Person {
Name = "Charlie", Age = 35 }
};
// 将 List<Person> 转换为 IQueryable<Person>
IQueryable<Person>