C# 泛型

16.1 泛型概念

泛型允许编写与类型无关的代码,提高复用性和类型安全。

非泛型问题

1
2
3
4
// 需要为每种类型写一个方法
int MaxInt(int a, int b) => a > b ? a : b;
double MaxDouble(double a, double b) => a > b ? a : b;
string MaxString(string a, string b) => a.CompareTo(b) > 0 ? a : b;

泛型解决方案

1
2
3
4
5
6
7
8
9
T Max<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) > 0 ? a : b;
}

// 使用
Console.WriteLine(Max(10, 20)); // 20
Console.WriteLine(Max(3.14, 2.72)); // 3.14
Console.WriteLine(Max("apple", "banana")); // banana

16.2 泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Repository<T>
{
private List<T> _items = new();

public void Add(T item) => _items.Add(item);

public T GetById(int id) => _items[id];

public IEnumerable<T> GetAll() => _items;

public int Count => _items.Count;
}

// 使用
var intRepo = new Repository<int>();
intRepo.Add(1);
intRepo.Add(2);

var stringRepo = new Repository<string>();
stringRepo.Add("Hello");

16.3 泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}

// 使用
int x = 10, y = 20;
Swap(ref x, ref y);
Console.WriteLine($"x={x}, y={y}"); // x=20, y=10

string s1 = "Hello", s2 = "World";
Swap(ref s1, ref s2);

16.4 类型约束

约束类型

约束 说明 示例
where T : struct 值类型 where T : struct
where T : class 引用类型 where T : class
where T : new() 有无参构造函数 where T : new()
where T : BaseClass 继承基类 where T : Animal
where T : Interface 实现接口 where T : IComparable

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 多个约束
public class Service<T> where T : class, IComparable<T>, new()
{
public T Create() => new T();

public int Compare(T a, T b) => a.CompareTo(b);
}

// 基类约束
public class Animal { }
public class Dog : Animal { }

public class Kennel<T> where T : Animal
{
public void Add(T animal) { }
}

16.5 泛型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface IRepository<T>
{
void Add(T item);
T GetById(int id);
IEnumerable<T> GetAll();
}

public class ProductRepository : IRepository<Product>
{
private List<Product> _products = new();

public void Add(Product item) => _products.Add(item);
public Product GetById(int id) => _products.FirstOrDefault(p => p.Id == id);
public IEnumerable<Product> GetAll() => _products;
}

16.6 协变与逆变

协变(out)

1
2
3
// IEnumerable<out T> 是协变的
IEnumerable<string> strings = new List<string> { "a", "b" };
IEnumerable<object> objects = strings; // 安全转换

逆变(in)

1
2
3
4
// Action<in T> 是逆变的
Action<object> actObject = obj => Console.WriteLine(obj);
Action<string> actString = actObject; // 安全转换
actString("Hello");

16.7 default 关键字

1
2
3
4
5
6
7
8
T GetDefault<T>()
{
return default; // 值类型返回 0,引用类型返回 null
}

Console.WriteLine(GetDefault<int>()); // 0
Console.WriteLine(GetDefault<string>()); // null
Console.WriteLine(GetDefault<bool>()); // False

16.8 泛型缓存

1
2
3
4
5
6
7
8
9
10
11
12
public static class Cache<T>
{
private static Dictionary<string, T> _cache = new();

public static void Set(string key, T value) => _cache[key] = value;

public static T Get(string key) => _cache.GetValueOrDefault(key);
}

// 使用
Cache<int>.Set("age", 25);
Console.WriteLine(Cache<int>.Get("age")); // 25

16.9 本章小结

  • 泛型提高代码复用性和类型安全
  • 泛型类、方法、接口支持类型参数
  • 类型约束限制泛型类型范围
  • out 协变,in 逆变
  • default 返回类型默认值

练习题

  1. 创建泛型方法 AreEqual<T>(T a, T b) 比较两个值
  2. 实现泛型栈 Stack<T>
  3. 使用类型约束创建泛型排序方法
  4. 以下代码能否编译?
    1
    2
    3
    class Test<T> { public T Value = default; }
    var t = new Test<int>();
    Console.WriteLine(t.Value);
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
C#基础教程 系列
第 16/20 篇
分享: