当前位置: 首页 > news >正文

【一文了解】C#泛型

目录

泛型

1.什么是泛型

为什么需要泛型?

2.类型参数

类型参数的命名规范

3.泛型结构分类

3.1.泛型类(最常用)

3.2.泛型方法

3.3.泛型接口

3.4.泛型委托

4.泛型约束

常用泛型约束

注意

泛型的优点

总结


       本篇文章来分享一下泛型,学会使用泛型能大大提升开发的效率。

泛型

1.什么是泛型

       在C#中,泛型(Generic)是一种允许定义不依赖于具体数据类型的类、接口、方法或委托的编程特性。它的核心思想是“将类型作为参数传递”,从而实现代码的复用性、类型安全性和性能优化,避免因处理不同类型而编写重复代码(例如,为int、string、自定义类分别编写相似逻辑)。

为什么需要泛型?

       解决“重复代码”与“类型安全”问题

在泛型出现之前,处理不同类型的相似逻辑通常有两种方案,但都存在明显缺陷:

1.使用object类型(非类型安全)

object是所有类型的基类,可存储任意类型数据,但需要频繁装箱/拆箱(值类型与object转换),不仅性能损耗大,还会丢失编译时类型检查(例如,将int类型的集合误存入string,编译不报错,运行时才崩溃)。

2.为每种类型写重复代码(低复用性)

若为int、string、Player等每种类型单独编写集合类(如IntList、StringList、PlayerList),逻辑完全相同,仅类型不同,会导致代码冗余、维护成本极高。

泛型的出现完美解决了这两个问题:在编译时确定类型,保证类型安全;同时复用一套逻辑,无需重复编码。

2.类型参数

       “类型参数”是泛型的核心语法,泛型的关键是通过<T>(T是“类型参数”的占位符,可自定义名称,如<TItem>、<TData>)声明 “类型变量”,在使用时传入具体类型(如List<int>、Dictionary<string, Player>)。

类型参数的命名规范

通常用单个大写字母表示(如T、TKey、TValue),遵循“见名知意”:

T:通用类型参数(如List<T>);

TKey:键类型(如Dictionary<TKey, TValue>);

TValue:值类型(如Dictionary<TKey, TValue>);

TResult:返回值类型(如Func<TResult>)。

3.泛型结构分类

3.1.泛型类(最常用)

       定义泛型类时,在类名后添加<类型参数>,内部可将T作为普通类型使用(如成员变量、方法参数、返回值)。

/// <summary>
/// 泛型类:T 是类型参数,代表“要传入的具体类型”
/// </summary>
public class GenericList<T>
{//用T定义成员变量(存储T类型的数组)private T[] itemArray;private int count;/// <summary>/// 构造函数:初始化数组容量/// </summary>/// <param name="capacity"></param>public GenericList(int capacity){itemArray = new T[capacity];//用T创建数组count = 0;}public void Add(T item)//泛型方法参数为T类型{if (count < itemArray.Length){itemArray[count] = item;//直接存储T类型,无需装箱count++;}}public T Get(int index)//泛型方法:返回值为T类型{if (index >= 0 && index < count){return itemArray[index];//直接返回T类型,无需拆箱}throw new ArgumentOutOfRangeException(nameof(index));}
}
public class Player
{public string name;public Player(string name){this.name = name;}
}
public class Test1:MonoBehaviour
{public void Start(){//1.使用 T 为 int 的泛型类var intList = new GenericList<int>(5);//明确指定T为intintList.Add(10);intList.Add(20);int num = intList.Get(0);//无需强制转换,编译时确认类型安全//2.使用 T 为 Player(自定义类)的泛型类var playerList = new GenericList<Player>(3);playerList.Add(new Player("张三"));Player player = playerList.Get(0); //直接获取Player类型//编译时报错(类型不匹配)intList.Add("abc"); //无法将string存入GenericList<int>}
}

3.2.泛型方法

       即使在非泛型类中,也可以定义泛型方法(方法名后加<T>),实现“同一方法处理不同类型”。

public class PrintHelper
{//泛型方法:T是方法的类型参数public void Print<T>(T value){Debug.Log($"值:{value},类型:{typeof(T).Name}");}
}
public class Test2 : MonoBehaviour
{public void Start(){//使用泛型方法PrintHelper helper = new PrintHelper();helper.Print(123);//值:123,类型:Int32helper.Print("Hello");//输出:值:Hello,类型:Stringhelper.Print<Person>(new Person());//输出:值:Player,类型:Player}
}

       类型推断调用泛型方法时,若能从参数类型推断出T,可省略<T>,如上述中的helper.Print(123)会自动推断T为int

3.3.泛型接口

       接口本质上定义了行为规范,但不实现行为。泛型接口允许在接口定义中使用类型参数,使接口能适配多种数据类型。

/// <summary>
/// 数据操作接口 用于数据的增删改查
/// </summary>
public interface IOperatable<T>
{/// <summary>/// 添加数据/// </summary>/// <param name="data"></param>public void Add(T data);/// <summary>/// 根据ID获取数据/// </summary>/// <param name="id"></param>/// <returns></returns>public T GetById(int id);/// <summary>/// 更新数据/// </summary>/// <param name="data"></param>public void Update(T data);/// <summary>/// 删除数据/// </summary>/// <param name="id"></param>public void Delete(int id);
}
public class User
{public int Id { get; set; }public string Name { get; set; }
}
//实现泛型接口(以用户数据为例)
public class UserOperation : IOperatable<User>
{public void Add(User data){Debug.Log($"添加用户:{data.Name}");}public User GetById(int id){return new User { Id = id, Name = "测试用户" };}public void Update(User data){Debug.Log($"更新用户:{data.Name}");}public void Delete(int id){Debug.Log($"删除用户 ID:{id}");}
}
public class Test3 : MonoBehaviour
{public void Start(){UserOperation user = new UserOperation();user.Add(new User { Id = 1, Name = "测试用户" });user.Update(new User { Id = 2, Name = "测试用户" });Debug.Log(user.GetById(2).Name);user.Delete(2);}
}

3.4.泛型委托

       泛型委托允许定义可接受不同类型参数的委托,用于传递具有通用逻辑的方法。

public class Student
{public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }
}
/// <summary>
/// 选择委托
/// 返回数据类型T的属性Tkey的值
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <typeparam name="Tkey">数据类型T的字段</typeparam>
/// <returns>选择的属性</returns>
public delegate TKey SelectHandler<T, TKey>(T t);
public class Test4 : MonoBehaviour
{private void Start(){//int[]int[] intArray = { 4, 1, 5, 0 };SortTestByProperty(intArray, (value) => { return value; });//string[]string[] stringArray = { "2", "a", "ab", "hello", "0" };SortTestByProperty(stringArray, (value) => { return value; });//Student[]Student[] studentArray ={new Student(){ Id=1001,Name="张三",Age=20 },new Student(){ Id=1003,Name="李四",Age=18 },new Student(){ Id=1002,Name="赵六",Age=21 },new Student(){ Id=1000,Name="王五",Age=19 }};SortTestByProperty(studentArray, (studentArray) => { return studentArray.Id; });SortTestByProperty(studentArray, (studentArray) => { return studentArray.Name; });SortTestByProperty(studentArray, (studentArray) => { return studentArray.Age; });}private void SortTestByProperty<T, Tkey>(T[] array, SelectHandler<T, Tkey> selectHandler)where Tkey : IComparable<Tkey>{Debug.Log(array.GetType() + "测试:");PrintSortedArray(array, selectHandler);}private void PrintSortedArray<T, Tkey>(T[] array, SelectHandler<T, Tkey> selectHandler)where Tkey : IComparable<Tkey>{string sortedStr = "";for (int i = 0; i < array.Length; i++){sortedStr += selectHandler(array[i]).ToString() + " ";}Debug.Log(sortedStr);}
}

       想要详细了解实现通用的排序功能可以参考【一文读懂】C#如何实现通用的排序功能

4.泛型约束

       泛型约束用于限制泛型类型参数的范围,确保类型参数满足特定条件(如继承自某个类、实现某个接口、具有特定构造函数等)。通过约束,可以让泛型代码更安全、更灵活,避免因类型参数不符合预期而导致的运行时错误。

常用泛型约束

约束说明
基类约束(where T : 基类名)要求类型参数T必须是指定基类或其派生类
接口约束(where T : 接口名)要求类型参数T必须实现指定接口
引用类型约束(where T : class)要求类型参数T必须是引用类型(如类、接口、委托等,不能是值类型如int、struct)
值类型约束(where T : struct)要求类型参数T必须是非空值类型(如int、struct、enum,但不能是nullable值类型如int?)
无参构造函数约束(where T : new())要求类型参数T必须具有公共无参构造函数(包括编译器自动生成的默认构造函数。当与其他约束一起使用时,new()约束必须最后指定
泛型参数约束(where T : U)要求类型参数T必须是另一个泛型参数U或其派生类型(常用于关联多个泛型参数)

       可单独使用,也可同时指定多个约束(用逗号分隔),实现更精确的限制。

//基类
public class Animal 
{ public void Eat() { }
}
//派生类
public class Dog : Animal { }
public class Cat : Animal { }//泛型类:基类约束,约束T必须继承自Animal
public class AnimalCare<T> where T : Animal
{public void Feed(T animal){animal.Eat();}
}
public class Test5:MonoBehaviour
{private void Start(){//使用:正确(Dog/Cat是Animal的子类)var dogCare = new AnimalCare<Dog>();var catCare = new AnimalCare<Cat>();//编译时报错(string不是Animal的子类)var error = new AnimalCare<string>();}
}

注意

       1.泛型不是“万能类型”:泛型的T是 “类型参数”,必须在使用时传入具体类型,不能直接使用List<T>(需指定List<int>等)。

       2.泛型不支持“值类型默认值直接赋值”:若T可能是值类型(如int),不能直接写T value = null(值类型不能为null),需用default(T)获取默认值(值类型默认0/false,引用类型默认null)。

       3.泛型类不能直接重载“基于类型参数的方法”:如void Method<T>(T value)和void Method<int>(int value)不允许,因为泛型实例化后会导致方法签名冲突。

泛型的优点

1.类型安全(编译时检查)

       泛型在编译时就确定具体类型,不允许存入不匹配的类型(如List<int>不能存string),避免运行时类型转换错误。

2.性能优化(避免装箱/拆箱)

       对于值类型(如int、struct),泛型直接存储原类型,无需像 object 那样进行 “装箱”(值类型转object)和 “拆箱”(object转值类型),减少性能损耗。

3.代码复用(一套逻辑适配多类型)

       泛型类/方法只需定义一次,即可处理任意类型(如List<T>可用于int、string、自定义类等),无需为每种类型写重复代码。

4.可读性与维护性强

       代码中明确标注类型参数(如List<Player>),语义清晰,后续修改逻辑只需改一处,无需修改所有类型的重复代码,复用性较好。

总结

       泛型是C#中提升代码复用性、类型安全性和性能的核心特性,其本质是“类型参数化”。通过<T>定义通用逻辑,使用时传入具体类型,既避免了object型的不安全问题,又能提高代码的复用性。

       好了,本次的分享到这里就结束啦,希望对你有所帮助~

http://www.dtcms.com/a/366821.html

相关文章:

  • 数据库集成:使用 SQLite 与 Electron
  • 新电脑硬盘如何分区?3个必知技巧避免“空间浪费症”!
  • [技术革命]Harmonizer:仅20MB模型如何实现8K图像_视频的完美和谐化?
  • 鸿蒙:AppStorageV2状态管理和数据共享
  • 泛型的通配符
  • axios请求缓存与重复拦截:“相同请求未完成时,不发起新请求”
  • TDengine TIMETRUNCATE 函数用户使用手册
  • 野火STM32Modbus主机读取寄存器/线圈失败(三)-尝试将存贮事件的地方改成数组(非必要解决方案)(附源码)
  • 腾讯云国际代理:如何在腾讯云GPU服务器上部署私有化大模型?附GPU简介
  • SQLmap 完整使用指南:环境搭建 + 命令详解 + 实操案例
  • 打开 solidworks当前文件 所在的文件夹 python pywin32
  • Effective Python 第10条 - 用赋值表达式减少重复代码
  • 上位机知识篇---conda run
  • KingbaseES一体化架构与多层防护体系如何保障企业级数据库的持续稳定与弹性扩展
  • 关于在自然语言处理深层语义分析中引入公理化体系的可行性、挑战与前沿展望
  • 谁才是企业级开源平台的优选?OpenCSG与Dify、Coze、Langflow、Ollama 的差异化之路
  • 深度学习——ResNet 卷积神经网络
  • 高并发商城 商品为了防止超卖,都做了哪些努力?
  • 2025国赛C题保姆级教程思路分析 NIPT 的时点选择与胎儿的异常判定
  • Spring Cloud Alibaba快速入门01
  • C语言结构体:轻松管理球员数据
  • SpringMVC的异常处理和拦截器
  • 【C语言】深入理解指针(4)
  • nextcyber——常见应用攻击
  • 一个老工程师的“新生”:良策金宝AI,让我重新爱上设计
  • [光学原理与应用-389]:设计 - 深紫外皮秒脉冲激光器 - 元件 - 1064nm种子光准直透镜
  • 2025年经管领域专业资格认证发展路径分析
  • 数据结构 之 【模拟实现哈希表】
  • Python 值传递 (Pass by Value) 和引用传递 (Pass by Reference)
  • 电池预测 | 第36讲 Matlab基于CNN-BiGRU-Attention的锂电池剩余寿命预测