C#之泛型🔥

1/21/2024 CSharp

# 什么是泛型

在C#中,泛型是一种强大的特性,它允许您编写可以在不同类型上进行参数化的代码。通俗地说,泛型允许您编写可以在不同类型(例如整数、字符串、自定义对象等)上执行相同逻辑的代码,而不必为每种类型编写不同的代码。这使得您的代码更具通用性和可重用性。通过泛型,您可以定义类、接口、方法和委托,这些可以接受类型参数,从而使其在编译时具有更高的类型安全性。使用泛型可以在编译时捕获类型错误,而不是在运行时。例如,您可以创建一个泛型类来存储任何类型的数据,而不是仅限于一种特定类型。这样的类在使用时可以根据需要指定所需的类型,从而增加了灵活性和可扩展性。

# 1、泛型类

public class GenericList<T>
{
    private T[] list;
    private int count;

    public GenericList(int capacity)
    {
        list = new T[capacity];
        count = 0;
    }

    public void Add(T item)
    {
        if (count < list.Length)
        {
            list[count] = item;
            count++;
        }
    }

    public T GetItem(int index)
    {
        if (index >= 0 && index < count)
        {
            return list[index];
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

使用泛型类:

GenericList<int> intList = new GenericList<int>(5);
intList.Add(10);
intList.Add(20);
int item = intList.GetItem(0); 
1
2
3
4

# 2、泛型方法

public class Utility
{
    public static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}
1
2
3
4
5
6
7
8
9

使用泛型方法:

int x = 10;
int y = 20;
Utility.Swap(ref x, ref y); 
1
2
3

# 3、泛型接口

public interface IRepository<T>
{
    void Add(T item);
    T GetById(int id);
    IEnumerable<T> GetAll();
}
1
2
3
4
5
6

使用泛型接口:

// 实现泛型接口的具体类
public class UserRepository : IRepository<User>
{
    private List<User> users = new List<User>();

    public void Add(User user)
    {
        users.Add(user);
    }

    public User GetById(int id)
    {
        return users.Find(u => u.Id == id);
    }

    public IEnumerable<User> GetAll()
    {
        return users;
    }
}

// 泛型类
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // 使用 UserRepository 实例
        var userRepository = new UserRepository();
        userRepository.Add(new User { Id = 1, Name = "Alice" });
        userRepository.Add(new User { Id = 2, Name = "Bob" });

        // 获取用户
        User user = userRepository.GetById(1);
        Console.WriteLine($"User with Id 1: {user.Name}");

        // 获取所有用户
        IEnumerable<User> allUsers = userRepository.GetAll();
        Console.WriteLine("All Users:");
        foreach (var u in allUsers)
        {
            Console.WriteLine($"Id: {u.Id}, Name: {u.Name}");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

在上面的示例中,UserRepository 类实现了这个泛型接口,但只适用于 User 类型。这样,您可以轻松地为不同类型的数据创建具有相同接口的存储库实现,从而提高了代码的灵活性和可维护性。

# 4、泛型委托

public delegate void ProcessData<T>(T data);

public class Processor
{
    public static void PrintData<T>(T data)
    {
        Console.WriteLine(data);
    }
}
1
2
3
4
5
6
7
8
9

使用泛型委托:

ProcessData<int> processInt = Processor.PrintData;
processInt(10); 
1
2

# 泛型约束

在C#中,泛型约束用于限制可以用作泛型类型参数的类型。通过约束,您可以为泛型类型参数指定必须满足的条件,从而增强代码的类型安全性和可读性。以下是一些常见的泛型约束:

1、where T : class:指定类型参数必须是引用类型(类)。

2、where T : struct:指定类型参数必须是值类型(结构体),这个约束使得类型参数不能是类。

3、where T : new():指定类型参数必须具有无参数的公共构造函数。

4、where T : <基类名>:指定类型参数必须是指定基类或其派生类。

5、where T : <接口名>:指定类型参数必须实现指定的接口。

这些约束可以单独使用,也可以结合在一起使用,以满足特定的需求。例如,您可以同时使用 class 和 new() 约束来确保类型参数是引用类型并且具有无参数的公共构造函数。使用泛型约束可以让您编写更安全、更清晰的泛型代码,并减少运行时错误的可能性。