C#学习记录-泛型

发布时间:2026/5/27 0:34:40

C#学习记录-泛型 在C#中泛型是一个非常核心的特性。它广泛应用与集合、委托、接口、方法、类以及很多.NET基础库中。比如我们经常看到这些写法ListT、DictionaryTKey,TValue、FuncT,TResult、NullableT。这些都和泛型有关。泛型的出现大大提高了代码的复用性类型安全性可维护性性能表现。所以泛型不仅仅是语法层面的知识点更是一种重要的编程思想。一、什么是泛型泛型可以简单理解为把类型当作参数传递。平时我们调用方法时传的时数据参数比如Add(1,2);而泛型做的事情是除了传数据还可以传类型参数。例如Listint Liststring这里的int和string就是传给泛型的类型参数。也就是说普通参数决定传什么值泛型参数决定用什么类型。1.1一个直观理解你可以把泛型想象成类型占位符。例如public class BoxT { public T Value { get; set; } }这里的T不是某个具体类型它只是一个占位符。等真正使用的时候才决定它是什么类型Boxint intBox new Boxint(); Boxstring strBox new Boxstring();这样同一套代码就能适合于不同类型。二、为什么需要泛型这是理解泛型最关键的一步。很多人可能会问没有泛型也能写程序为啥还要用泛型答案是因为泛型可以同时解决代码复用类型安全和性能问题。2.1不适用泛型时的问题在泛型出现之前如果想写一个能存放任意类型的容器通常会用object。例如public class Box { public object Value { get; set; } }使用的时候Box box1 new Box(); box1.Value 100; Box box2 new Box(); box2.Value Hello;看起来似乎也能达到通用的目的但这里有几个问题。1.类型不安全Box box new Box(); box.Value Hello; int num (int)box.Value; // 运行时出错因为object可以接收任何类型所以编译器无法提前检查错误。只有运行时强制转换时才会发现类型不匹配。2.需要频繁强制转换string text (string)box.Value;这种写法不仅麻烦而且容易出错。3.值类型会发生装箱和拆箱object obj 123; // 装箱 int num (int)obj; // 拆箱这会带来额外的性能开销。2.2泛型带来的好处泛型的出现正式为了改进这些问题。public class BoxT { public T Value { get; set; } }使用时Boxint box new Boxint(); box.Value 100; int num box.Value; // 不需要强转这样就有几个明显的优势类型明确编译器检查不需要强制转换避免不必要的装箱拆箱。三、泛型类泛型最常见的用法之一就是泛型类。public class BoxT { public T Value { get; set; } public void ShowValue() { Console.WriteLine(Value); } }这里的T表示Value的类型不固定具体是什么类型由创建对象时决定。就像下面这样using System; public class BoxT { public T Value { get; set; } public void ShowValue() { Console.WriteLine(Value); } } public class Program { public static void Main() { Boxint intBox new Boxint(); intBox.Value 123; intBox.ShowValue(); Boxstring strBox new Boxstring(); strBox.Value 你好泛型; strBox.ShowValue(); } }这个例子在于我们不需要专门写IntBox和StringBox只需要写一个BoxT就能支持不同的类型。这就是泛型代码复用的体现。四、泛型方法除了类可以使用泛型方法也可以使用泛型。示例using System; public class Program { public static void SwapT(ref T a, ref T b) { T temp a; a b; b temp; } public static void Main() { int x 10; int y 20; Swap(ref x, ref y); Console.WriteLine($x{x}, y{y}); string s1 Hello; string s2 World; Swap(ref s1, ref s2); Console.WriteLine($s1{s1}, s2{s2}); } }这里的SwapT表示这个方法适用于任意类型T,只要两个参数类型一致就可以使用同一套交换逻辑。五、泛型接口接口同样也可以定义为泛型接口。public interface IRepositoryT { void Add(T item); T GetById(int id); }这里表示这个接口可以用于不同的数据类型T在具体实现时再决定。示例using System; public interface IRepositoryT { void Add(T item); T GetById(int id); } public class User { public int Id { get; set; } public string Name { get; set; } } public class UserRepository : IRepositoryUser { public void Add(User item) { Console.WriteLine($添加用户{item.Name}); } public User GetById(int id) { return new User { Id id, Name 张三 }; } } public class Program { public static void Main() { IRepositoryUser repo new UserRepository(); repo.Add(new User { Id 1, Name 李四 }); User user repo.GetById(1); Console.WriteLine($获取到用户{user.Name}); } }六、泛型委托委托也可以用泛型。实际上我们常见的ActionT、FuncT,TResult、PredicateT本质上都是泛型委托。七、自定义泛型委托public delegate T TransformerT(T input);表示输入参数类型是T,返回值类型也是T。示例using System; public delegate T TransformerT(T input); public class Program { public static void Main() { Transformerint square x x * x; Console.WriteLine(square(5)); Transformerstring toUpper s s.ToUpper(); Console.WriteLine(toUpper(hello)); } }八、常见泛型类型参数命名在写泛型时虽然参数名可以随便取但通常会遵循一些约定。T表示一个普通类型参数TKey表示键类型TValue表示值类型TResult表示返回结果类型TItem表示集合中的元素类型TSource表示源数据类型九、C#中常见的泛型集合泛型在.NET中最常见的应用就是集合类。在没有泛型的时代集合通常保存的是object使用起来不够安全。而泛型集合可以明确元素类型。9.1Listusing System; using System.Collections.Generic; public class Program { public static void Main() { Listint numbers new Listint(); numbers.Add(10); numbers.Add(20); numbers.Add(30); foreach (int n in numbers) { Console.WriteLine(n); } } }9.2DictionaryTKey,TValueusing System; using System.Collections.Generic; public class Program { public static void Main() { Dictionaryint, string students new Dictionaryint, string(); students.Add(1, 张三); students.Add(2, 李四); Console.WriteLine(students[1]); Console.WriteLine(students[2]); } }9.3QueueQueuestring queue new Queuestring(); queue.Enqueue(A); queue.Enqueue(B); Console.WriteLine(queue.Dequeue());9.4StackStackint stack new Stackint(); stack.Push(1); stack.Push(2); Console.WriteLine(stack.Pop());十、泛型约束泛型虽然灵活但有时候我们不希望类型参数完全任意而是希望它满足某些条件。这时就需要使用泛型约束约束的作用就是限制泛型参数必须符合某些限制。下面是整理的泛型约束10.1where T: calss表示T必须是引用类型public class MyClassT where T : class { }10.2where T : struct表示T必须是值类型。public class MyStructBoxT where T : struct { }10.3where T : new()表示T必须有一个公共的无参构造函数public class FactoryT where T : new() { public T Create() { return new T(); } }10.4 where T : BaseClass表示T必须继承某个基类public class Animal { public void Eat() { Console.WriteLine(动物在吃东西); } } public class CageT where T : Animal { public void Feed(T animal) { animal.Eat(); } }10.5where T : IInterface表示T必须实现某个接口public interface IRun { void Run(); } public class RunnerT where T : IRun { public void Start(T obj) { obj.Run(); } }10.6多个约束一起使用public class MyClassT where T : class, new() { }表示T必须是引用类型并且有无参构造函数。十一、泛型方法的类型推断调用泛型方法时很多时候不需要手动写出类型参数编译器可以自动推断。示例using System; public class Program { public static void PrintT(T value) { Console.WriteLine(value); } public static void Main() { Printint(100); Printstring(Hello); // 也可以省略类型参数 Print(200); Print(World); } }编译器可以根据传入参数自动推断T的类型。这让泛型在实际使用中更加简洁。十二、总结C#泛型是一种非常重要的语言特性它允许我们把类型作为参数传递从而实现逻辑复用、类型安全、性能优化。学习泛型时不能只停留在ListT这样的表面语法更要理解其背后的思想。十三、参考链接Microsoft Learn - 泛型C# 编程指南https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/Microsoft Learn - 泛型类型参数https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-type-parametersMicrosoft Learn - 泛型中的约束https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

相关新闻