404 Not Found

404 Not Found


nginx

集合与泛型

泛型概念与动机

泛型允许我们编写类型参数化的代码,实现类型安全代码复用。在没有泛型之前,ArrayList存储object类型,值类型存入时会装箱(boxing),取出时需拆箱(unboxing),既损失性能又缺乏编译期类型检查。

示例

CSHARP
using System;
using System.Collections;

ArrayList list = new ArrayList();
list.Add(42);
list.Add("hello");

int value = (int)list[0];

foreach (object item in list)
{
    Console.WriteLine(item);
}
▶ 试一试
TEXT
42
hello

泛型版本则在编译期确定元素类型,无需装箱拆箱,错误在编译时即可发现:

CSHARP
using System;
using System.Collections.Generic;

List<int> numbers = new List<int>();
numbers.Add(42);

foreach (int n in numbers)
{
    Console.WriteLine(n);
}
TEXT
42

List<T> 动态列表

List<T>是最常用的泛型集合,支持动态扩容的有序列表,提供丰富的增删查改方法。

示例

CSHARP
using System;
using System.Collections.Generic;

List<string> fruits = new List<string> { "apple", "banana", "cherry" };

fruits.Add("date");
fruits.AddRange(new string[] { "elderberry", "fig" });
fruits.Insert(1, "blueberry");

bool hasApple = fruits.Contains("apple");
int idx = fruits.IndexOf("cherry");

string found = fruits.Find(f => f.StartsWith("b"));
List<string> allB = fruits.FindAll(f => f.StartsWith("b"));

fruits.Sort();
fruits.ForEach(f => Console.WriteLine(f));

Console.WriteLine("---");
Console.WriteLine($"Count: {fruits.Count}");
Console.WriteLine($"Has apple: {hasApple}");
Console.WriteLine($"Cherry index: {idx}");
Console.WriteLine($"First b-word: {found}");
Console.WriteLine($"All b-words: {string.Join(", ", allB)}");
▶ 试一试
TEXT
apple
blueberry
banana
cherry
date
elderberry
fig
---
Count: 7
Has apple: True
Cherry index: 3
First b-word: blueberry
All b-words: blueberry, banana

删除元素

CSHARP
using System;
using System.Collections.Generic;

List<int> nums = new List<int> { 10, 20, 30, 40, 50 };

nums.Remove(30);
nums.RemoveAt(0);

Console.WriteLine(string.Join(", ", nums));
Console.WriteLine($"Count: {nums.Count}");
TEXT
20, 40, 50
Count: 3

Dictionary<TKey, TValue> 字典

Dictionary<TKey, TValue>存储键值对,通过键快速查找值,查找时间接近O(1)。

示例

CSHARP
using System;
using System.Collections.Generic;

Dictionary<string, int> scores = new Dictionary<string, int>
{
    { "Alice", 90 },
    { "Bob", 85 }
};

scores.Add("Charlie", 78);
scores["Bob"] = 92;

bool exists = scores.ContainsKey("Alice");

if (scores.TryGetValue("Dave", out int daveScore))
{
    Console.WriteLine($"Dave: {daveScore}");
}
else
{
    Console.WriteLine("Dave not found");
}

scores.Remove("Charlie");

Console.WriteLine($"Count: {scores.Count}");
Console.WriteLine("---Keys---");
foreach (string key in scores.Keys)
{
    Console.WriteLine(key);
}
Console.WriteLine("---Values---");
foreach (int val in scores.Values)
{
    Console.WriteLine(val);
}
Console.WriteLine("---Pairs---");
foreach (KeyValuePair<string, int> kv in scores)
{
    Console.WriteLine($"{kv.Key}: {kv.Value}");
}
▶ 试一试
TEXT
Dave not found
Count: 2
---Keys---
Alice
Bob
---Values---
90
92
---Pairs---
Alice: 90
Bob: 92

泛型方法与泛型类

泛型不限于集合,方法和类也可以使用类型参数,使算法适用于多种类型。

示例

CSHARP
using System;

T Max<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) >= 0 ? a : b;
}

Console.WriteLine(Max(3, 7));
Console.WriteLine(Max("apple", "banana"));
▶ 试一试
TEXT
7
banana

泛型类示例:

CSHARP
using System;

class Box<T>
{
    private T _value;

    public Box(T value)
    {
        _value = value;
    }

    public T Value => _value;

    public override string ToString() => $"Box<{typeof(T).Name}>: {_value}";
}

Box<int> intBox = new Box<int>(42);
Box<string> strBox = new Box<string>("hello");

Console.WriteLine(intBox);
Console.WriteLine(strBox);
TEXT
Box<Int32>: 42
Box<String>: hello

泛型约束

通过where关键字对类型参数施加约束,限定可使用的类型范围。

示例

CSHARP
using System;

class Repository<T> where T : class, new()
{
    public T Create()
    {
        T instance = new T();
        Console.WriteLine($"Created: {typeof(T).Name}");
        return instance;
    }
}

class Player
{
    public string Name { get; set; } = "Unknown";
}

Repository<Player> repo = new Repository<Player>();
Player p = repo.Create();
Console.WriteLine($"Name: {p.Name}");
▶ 试一试
TEXT
Created: Player
Name: Unknown

常用约束一览:

约束 说明
where T : class T必须是引用类型
where T : struct T必须是值类型
where T : new() T必须有无参公共构造函数
where T : IFoo T必须实现IFoo接口
where T : Base T必须继承Base类

Queue<T> 队列

队列是先进先出(FIFO)数据结构,Enqueue入队,Dequeue出队。

示例

CSHARP
using System;
using System.Collections.Generic;

Queue<string> queue = new Queue<string>();
queue.Enqueue("first");
queue.Enqueue("second");
queue.Enqueue("third");

string front = queue.Peek();
Console.WriteLine($"Peek: {front}");

while (queue.Count > 0)
{
    Console.WriteLine($"Dequeue: {queue.Dequeue()}");
}
▶ 试一试
TEXT
Peek: first
Dequeue: first
Dequeue: second
Dequeue: third

Stack<T> 栈

栈是后进先出(LIFO)数据结构,Push入栈,Pop出栈。

示例

CSHARP
using System;
using System.Collections.Generic;

Stack<int> stack = new Stack<int>();
stack.Push(10);
stack.Push(20);
stack.Push(30);

int top = stack.Peek();
Console.WriteLine($"Peek: {top}");

while (stack.Count > 0)
{
    Console.WriteLine($"Pop: {stack.Pop()}");
}
▶ 试一试
TEXT
Peek: 30
Pop: 30
Pop: 20
Pop: 10

集合初始化器

集合初始化器允许在创建集合时直接填充元素,语法简洁直观。

示例

CSHARP
using System;
using System.Collections.Generic;

List<int> nums = new List<int> { 1, 2, 3, 4, 5 };
Dictionary<string, double> prices = new Dictionary<string, double>
{
    { "coffee", 3.5 },
    { "tea", 2.8 },
    { "juice", 4.0 }
};

Console.WriteLine(string.Join(", ", nums));
foreach (var kv in prices)
{
    Console.WriteLine($"{kv.Key}: ${kv.Value}");
}
▶ 试一试
TEXT
1, 2, 3, 4, 5
coffee: $3.5
tea: $2.8
juice: $4

IEnumerable 与迭代器初识

IEnumerable<T>是所有可遍历集合的基础接口,foreach循环正是依赖它工作。使用yield return可以轻松编写自定义迭代器(详细内容见第35课)。

示例

CSHARP
using System;
using System.Collections.Generic;

IEnumerable<int> GetRange(int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        yield return i;
    }
}

foreach (int n in GetRange(1, 5))
{
    Console.WriteLine(n);
}
▶ 试一试
TEXT
1
2
3
4
5
💡 yield return让方法变为迭代器,每次调用返回一个值并暂停,下次迭代从暂停处继续。完整讲解见第35课。

❓ 常见问题

Q List&lt;T>和数组有什么区别?
A List&lt;T>可动态增删元素,数组长度固定;List内部基于数组实现,扩容时自动创建更大数组并复制元素。
Q Dictionary添加重复键会怎样?
A 使用Add方法添加重复键会抛出ArgumentException;使用索引器赋值dict[key] = value则会覆盖已有值。
Q where T : class和where T : struct能同时使用吗?
A 不能,class约束引用类型,struct约束值类型,二者互斥。
Q Queue和Stack能用foreach遍历吗?
A 可以,foreach遍历不会移除元素,仅读取。Dequeue和Pop才会移除。
Q 泛型约束where T : new()为什么需要?
A 保证可以在方法内使用new T()创建实例,没有此约束编译器不允许对T使用new操作符。

📖 小节

📝 作业

  1. 创建一个List<double>,添加5个成绩,使用FindAll筛选出80分以上的成绩并输出
  2. 创建一个Dictionary<string, string>存储3个省会城市与省份的对应关系,实现根据城市名查找省份(使用TryGetValue
  3. 编写泛型方法T Clamp<T>(T value, T min, T max) where T : IComparable<T>,将值限制在[min, max]范围内
  4. 使用Queue<string>模拟打印队列:入队3个文档名,依次出队并打印
  5. 编写一个IEnumerable<int>迭代器方法,使用yield return生成斐波那契数列的前10项
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏