01-C#.Net-泛型-学习笔记

2021-05-22 .NET高级班 01-泛型 原理图 阅读详情

一、泛型的概念与引入

1.1 什么是泛型

  • 泛:宽泛的、不确定的
  • 型:类型
  • 泛型:不确定的类型,延迟声明的类型

1.2 为什么需要泛型

传统方案的问题:

// 方案1:为每种类型写一个方法 - 代码冗余
public static void ShowInt(int iParameter) { }
public static void ShowString(string sParameter) { }
public static void ShowDateTime(DateTime dtParameter) { }
// 如果有100个类型,需要100个方法?

// 方案2:使用Object类型 - 存在性能和类型安全问题
public static void ShowObject(object oParameter) 
{
    // 问题1:装箱拆箱导致性能损失
    // 问题2:类型不安全,编译时无法检测错误
    // 问题3:无法直接访问具体类型的成员
}

泛型方案:

// 一个方法满足所有类型需求
public static void Show<T>(T tParameter)
{
    Console.WriteLine($"Type={tParameter.GetType().Name}");
}

// 调用方式
Show<int>(123);        // 显式指定类型
Show(123);             // 类型推导,可省略尖括号
Show<string>("hello");

1.3 泛型的优势

  1. 性能优异:避免装箱拆箱操作
  2. 代码复用:一个方法/类满足多种类型需求
  3. 类型安全:编译时类型检查,避免运行时错误
  4. 可读性强:代码意图清晰

性能对比示例:

// 1亿次调用性能测试
// 普通方法:最快
// Object方法:慢(装箱拆箱)
// 泛型方法:与普通方法性能相当

二、泛型的声明与原理

2.1 泛型的声明特点

  • 延迟声明:声明时使用占位符T,调用时才确定具体类型
  • 类型参数:T称为类型参数或类型变量
  • 尖括号语法:<T> 用于声明泛型

2.2 底层原理

// 在底层,泛型会生成带有反引号的类型
List<T>                    // 底层:List`1[T]
Dictionary<TKey, TValue>   // 底层:Dictionary`2[TKey,TValue]
  • 泛型由 .NET Framework 2.0 引入
  • 需要编译器和CLR运行时同时支持
  • 泛型不是语法糖,而是框架升级支持的特性

三、泛型的多种应用

3.1 泛型方法

public static void Show<T>(T tParameter)
{
    Console.WriteLine(tParameter.GetType().Name);
}

3.2 泛型接口

public interface GenericInterface<T>
{
    T Show();
}

// 实现方式1:指定具体类型
public class ChildClass : GenericInterface<int>
{
    public int Show() { return 0; }
}

// 实现方式2:保持泛型
public class ChildClass<S> : GenericInterface<S>
{
    public S Show() { return default(S); }
}

3.3 泛型类

// 多个类型参数
public class GenericClass<T, S, X, Y>
{
    public void Show(T t) { }
}

// 抽象泛型类
public abstract class GenericAbstractClass<T>
{
    public void Show(T t) { }
}

3.4 泛型委托

public delegate T GenericDelegate<T>();

// 使用
GenericDelegate<string> del = () => "hello";
GenericDelegate<int> del2 = () => 123;

四、泛型约束

4.1 为什么需要约束

// 没有约束时,无法访问类型的特定成员
public static void Show<T>(T t)
{
    // t.Id;  // 编译错误:T没有Id属性
}

4.2 六种约束类型

1. 基类约束 where T : BaseClass
public static void ShowBase<T>(T t) where T : People
{
    // 可以访问People的成员
    Console.WriteLine(t.Id);
    Console.WriteLine(t.Name);
}

// 调用
ShowBase<People>(people);      // ✓
ShowBase<Chinese>(chinese);    // ✓ Chinese继承People
ShowBase<Japanese>(japanese);  // ✗ Japanese不继承People
2. 接口约束 where T : IInterface
public static void ShowInterface<T>(T t) where T : ISports
{
    t.Pingpang(); // 可以调用接口方法
}

// 调用
ShowInterface<Chinese>(chinese);   // ✓ Chinese实现了ISports
ShowInterface<Japanese>(japanese); // ✓ Japanese实现了ISports
3. 引用类型约束 where T : class
public static void ShowClass<T>(T t) where T : class
{
    // T必须是引用类型(类)
}

ShowClass<People>(people);  // ✓
ShowClass<int>(123);        // ✗ int是值类型
4. 值类型约束 where T : struct
public static void ShowStruct<T>(T t) where T : struct
{
    T newT = new T(); // 值类型可以直接new
}

ShowStruct<int>(123);               // ✓
ShowStruct<DateTime>(DateTime.Now); // ✓
ShowStruct<People>(people);         // ✗ People是引用类型
5. 无参构造函数约束 where T : new()
public static void ShowNew<T>(T t) where T : new()
{
    T newT = new T(); // 可以创建实例
}
6. 枚举约束 where T : Enum
public static void ShowEnum<T>(T t) where T : Enum
{
    // T必须是枚举类型
}

ShowEnum<UserType>(UserType.Normal); // ✓

4.3 多重约束

// 父子关系约束
public static void ShowParent<T, S>(T t, S s) where T : S
{
    // T和S要么是同一类型,要么T是S的子类
}

// 多个约束
public static void ShowTest<T, S>(T t, S s) 
    where T : class 
    where S : struct
{
}

五、泛型缓存

5.1 普通字典缓存

public class DictionaryCache
{
    private static Dictionary<Type, string> _TypeTimeDictionary = null;
    
    static DictionaryCache()
    {
        _TypeTimeDictionary = new Dictionary<Type, string>();
    }
    
    public static string GetCache<T>()
    {
        Type type = typeof(T);
        if (!_TypeTimeDictionary.ContainsKey(type))
        {
            _TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now}";
        }
        return _TypeTimeDictionary[type];
    }
}

5.2 泛型缓存

public class GenericCache<T>
{
    private static string _TypeTime = "";
    
    // 静态构造函数:每个T类型只执行一次
    static GenericCache()
    {
        _TypeTime = $"{typeof(T).FullName}_{DateTime.Now}";
    }
    
    public static string GetCache()
    {
        return _TypeTime;
    }
}

泛型缓存的特点:

  • 每个不同的T类型,会生成一个独立的类副本
  • 每个副本有自己的静态字段和静态构造函数
  • GenericCache<int> 和 GenericCache<string> 是完全不同的类
  • 应用场景:ORM框架中的类型缓存

六、协变与逆变

6.1 问题引入

Animal animal = new Cat(); // ✓ 子类可以赋值给父类

List<Animal> list1 = new List<Animal>(); // ✓
List<Animal> list2 = new List<Cat>();    // ✗ 编译错误
// 一只猫是动物,但一堆猫不是一堆动物?

6.2 协变(Covariance)- out

定义:允许使用更具体的派生类型(子类)

// out关键字:T只能作为返回值,不能作为参数
public interface ICustomerListOut<out T>
{
    T Get();           // ✓ 可以作为返回值
    // void Show(T t); // ✗ 不能作为参数
}

// 使用
IEnumerable<Animal> list1 = new List<Animal>(); // ✓
IEnumerable<Animal> list2 = new List<Cat>();    // ✓ 协变

Func<Animal> func = new Func<Cat>(() => new Cat()); // ✓

原理:右边是Cat,左边是Animal

  • 调用Get()返回Cat,赋值给Animal变量 ✓ 安全
  • 如果允许Show(Animal),实际需要Cat ✗ 不安全

6.3 逆变(Contravariance)- in

定义:允许使用更泛化的基类型(父类)

// in关键字:T只能作为参数,不能作为返回值
public interface ICustomerListIn<in T>
{
    void Show(T t);    // ✓ 可以作为参数
    // T Get();        // ✗ 不能作为返回值
}

// 使用
ICustomerListIn<Cat> list1 = new CustomerListIn<Cat>();    // ✓
ICustomerListIn<Cat> list2 = new CustomerListIn<Animal>(); // ✓ 逆变

Action<Animal> action = new Action<Cat>(c => { }); // ✗
Action<Cat> action2 = new Action<Animal>(a => { }); // ✓ 逆变

原理:右边是Animal,左边是Cat

  • 调用Show(Cat),传入Cat,Animal可以接收 ✓ 安全
  • 如果允许Get()返回Animal,赋值给Cat ✗ 不安全

6.4 协变+逆变组合

public interface IMyList<in TIn, out TOut>
{
    void Show(TIn t);   // in:只能作为参数
    TOut Get();         // out:只能作为返回值
    TOut Do(TIn t);     // 组合使用
}

// 使用
IMyList<Cat, Animal> list1 = new MyList<Cat, Animal>();    // ✓
IMyList<Cat, Animal> list2 = new MyList<Cat, Cat>();       // ✓ 协变
IMyList<Cat, Animal> list3 = new MyList<Animal, Animal>(); // ✓ 逆变
IMyList<Cat, Animal> list4 = new MyList<Animal, Cat>();    // ✓ 协变+逆变

6.5 记忆口诀

  • 协变(out):出去的,返回值,子类→父类,"出口放宽"
  • 逆变(in):进来的,参数,父类→子类,"入口放宽"
  • 限制:只适用于泛型接口和泛型委托

七、实际应用示例

7.1 分页结果类

public class PageResult<T> where T : class
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public List<T> DataList { get; set; }
}

// 使用
PageResult<User> userPage = new PageResult<User>();
PageResult<Product> productPage = new PageResult<Product>();

7.2 返回默认值

public S Show<S>()
{
    // return new S();     // ✗ S可能没有无参构造函数
    return default(S);     // ✓ 返回类型的默认值
}

八、关键要点总结

  1. 泛型不是语法糖,是框架升级支持的特性(.NET 2.0)
  2. 性能优势:避免装箱拆箱,与普通方法性能相当
  3. 类型安全:编译时检查,避免运行时错误
  4. 泛型约束:要么不让进,进来就保证安全
  5. 泛型缓存:每个类型生成独立副本,常驻内存
  6. 协变逆变:只针对接口和委托,增加类型转换的灵活性
  7. 应用场景:集合类、ORM框架、通用工具类、API返回结果封装
从0到100%掌握 C# AOT 的泛型、序列化与反射问题:轻松避开坑洼之路 泛型是一种强大的语言特性,允许你在定义类、接口、方法时使用类型参数。这样,你可以在不牺牲类型安全的情况下重用代码。但是,当你在 AOT 编程环境中使用泛型时,事情就没有那么简单了。序列化是将对象转换为可以存储或传输的格式的过程,反序列化则是将这些格式还原为对象的过程。在 AOT 编程中,序列化和反序列化常常涉及大量反射操作,而反射在 AOT 编译中是有限制的。反射允许你在运行时检查和操作类型、方法、属性等。然而,在 AOT 编程中,反射操作受到很大限制,因为编译器无法预测所有可能的反射操作。 阅读详情

相关推荐

【医学影像】超声(UltraSound)影像的增强与目标检测

文章目录1 综述1.1 超声发展简史1.2 超声类型2 超声影像的去噪与增强2.1 各向异性扩散去噪模型2.2 色彩恢复的视网膜增强算法SSR和MSR2.3 MSRCR增强算法(基于MSR算法改进版)2.4 参考文献3 超声影像的目标识别3.1 基于跨模态数据迁移进行DL训练3.2 参考文献 1 综述 因工作需要,最近调研了超声(UltraSound)影像的相关知识,包含超声类型、超声影像的去噪和增强,目标检测等,并对相关资料文献进行梳理整合。 本人另外一篇博文主要讲了UltraSound 影像与CT/MR

muyijames的博客 1万+

C#中的泛型

interface [接口名] [接口体]声明泛型接口时,与声明一般接口的唯一区别是增加了一个。一般来说,声明泛型接口与声明非泛型接口遵循相同的规则。泛型类型声明所实现的接口必须对所有可能的构造类型都保持唯一。否则就无法确定该为某些构造类型调用哪个方法。在实例化泛型时也可以使用约束对类型参数的类型种类施加限制,约束是使用where上下文关键字指定的。下面列出了6种类型的约束。约束说明T:结构类型参数必须是值类型。可以指定除Nullable以外的任何值类型T:类。

wenchm的博客 1520

Cadence PCB SI仿真实战:如何手动添加VIA过孔模型提升板级链路精度

本文详细介绍了在Cadence PCB SI仿真中如何手动添加VIA过孔模型以提升DDR4/5设计的精度。通过参数化建模和三维全波建模技术,工程师可以有效解决高速设计中的信号完整性问题,显著降低仿真误差。文章还提供了实战案例和常见问题解决方案,帮助优化板级链路性能。

weixin_28683391的博客 403

架构师进阶之路_01泛型

泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性。泛型为.NET框架引入了类型参数(type parameters)的概念。类型参数使得设计类和方法时,不必确定一个或多个具体参数,其的具体参数可延迟到客户代码中声明、实现。这意味着使用泛型的类型参数T,写一个类MyList<T>,客户代码可以这样调用:MyList<int>, MyList<...

不求上进的码农的博客 199

dify源码本地部署,dify k8s部署

Dify.AI 是一款简单易用且开源的 LLMOps 平台,帮助开发者更简单、更快速地创建 AI 应用。它的核心理念是通过可声明式的 YAML 文件定义 AI 应用的各个方面,包括 Prompt、上下文和插件等。Dify 提供了可视化的 Prompt 编排、运营、数据集管理等功能。这些功能使得开发者能够在数天内完成 AI 应用的开发,或将 LLM 快速集成到现有应用中,并进行持续运营和改进。

shykevin的博客 1014

2026年5月典型AI应用落地

2026年5月AI应用全面进入商用化阶段,呈现三大特征:产业端深度渗透核心业务场景,如法律领域AI合同处理系统完成8500万元项目;消费端实现规模普及,京东618全场景AI化,智能家居产品涌现;基础设施层面运营商推出词元套餐降低使用门槛。政策法规与国际合作同步推进,日本通过首部AI专项法律,中国举办多场产业对接会。AI应用从技术验证转向实效产出,进入精算回报的成熟期。

AI_Encyc的博客 2629

.NET泛型02,泛型的使用

在" .NET泛型01,为什么需要泛型,泛型基本语法"中,了解了泛型的基本概念,本篇偏重于泛型的使用。主要包括: ■ 泛型方法重载需要注意的问题■ 泛型的类型推断■ 泛型方法也可以有约束■ 泛型接口■ 泛型委托■ 使用EventHandler<TEventArgs>事件泛型 泛型方法重载需要注意的问题 public class MyArray<T> { ...

weixin_30275415的博客 147

【编号326】全国地灾数据全国25万多个地质灾害点空间分布数据

全国地质灾害点分布数据涵盖25万多个各类灾害点信息,为灾害防治提供重要依据。通过分析灾害分布规律,可识别高风险区,指导监测预警、应急避险和基础设施建设。多地成功案例(如湖北兴山崩塌避险、重庆长寿滑坡预警、湖南"挪床行动")证明,提前预警和科学防治能有效避免人员伤亡。云南省2024年通过预警体系成功避让31起灾害,保护1342人生命安全。这些实践表明,地质灾害防治需结合科技手段与群防体系,实现早预警、早处置。

千奇百怪无奇不有 1467

C#.Net筑基-泛型T & 协变逆变

泛型(Generics)是C#中的一种强大的强类型扩展机制,在申明时用“占位符”类型参数“T”定义一个“模板类型”,比较类似于C++中的模板。泛型在使用时指定具体的T类型,从而方便的封装、复用代码,提高类型的安全性,减少类型转换和装箱。泛型就是为代码能跨类型复用而设计的,轻松复用代码逻辑,如List<T>Queue<T>。用泛型参数来代替object,可以减少大量装箱、拆箱,显著提高代码性能,及代码安全性。比如C#中的List<T>就是泛型版的ArrayList就是泛型版的Hashtable。

weixin_42570296的博客 112

基于 YOLOv8 的自动驾驶车辆障碍物检测与避障路径规划

本文探讨基于YOLOv8的自动驾驶车辆障碍物检测与避障路径规划系统。在感知层面,采用YOLOv8模型实现多类别障碍物识别,结合单目视觉距离估计和ByteTrack多目标跟踪算法,完成障碍物的稳定检测与运动状态估计。在路径规划层面,设计分层规划架构,融合全局路径规划与局部避障算法,重点实现Frenet坐标系下的采样优化算法和基于模型预测控制的实时避障轨迹生成。通过完整的代码实现与仿真实验,验证系统在复杂交通场景下的有效性与实时性,为自动驾驶开发者提供从理论到实践的完整技术参考。 关键词:YOLOv8;自动驾驶

huanghm88的专栏 248

uniapp [全端兼容] - 实现人脸识别对比+人脸相似度,从相册选择或拍照上传两张图片进行人脸对比得到人脸相似度及判断是否是同一个人,人脸活体检测及对比(免费纯前端方案+调用第三方API接口方案)

uniApp人脸对比 人脸对比识别 人脸对比相似度,uniapp实现获取手机摄像头权限,调用相机拍照实现人脸识别相似度对比,uniapp实现人脸识别对比,计算两张人脸的相似度,从而判断是否同一个人,并给出相似度评分。uniapp H5网页如何实现人脸相似度比对,uniapp纯前端怎么对比两张图片的相似度,计算两幅图像的相似度,图像相似度对比方法,UNI-APP人脸相似度对比,快速识别两张照片的相似程度(用百分比)图片相似度对比在线测试,图片相似度对比,uniapp微信小程序人脸相似度对比分析,uniapp

🏆 前端领域优质创作者 5016

C# 泛型(Generic)

Generic - 未注册的,泛型编程。 泛型(Generic)的特性 使用泛型是一种增强程序功能的技术,具体表现在以下几个方面: 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。 您可以创建泛型集合类。.NET 框架类库在System.Collections.Generic命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代System.Collection...

知识无止境 1193

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

01-C#.Net-泛型-面试题

出题意图:考察对泛型基本概念的理解,以及是否能说清楚它解决了什么问题。答泛型是一种"延迟声明类型"的机制。声明时用占位符T代替具体类型,调用时再指定。性能问题:用object作为通用参数时,值类型会发生装箱/拆箱,有额外的堆分配和 CPU 开销。泛型在 CLR 层面对值类型生成专用代码,完全避免装箱,性能等同于专用方法。类型安全问题object参数在编译期无法检查类型,强制转换错误只能在运行时暴露。泛型在编译期就能检查类型,错误提前发现。// 泛型方法:一个方法,任意类型,无装箱,编译期安全。

噢~ 315

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

简单分析.net泛型中的类型参数

     一位朋友询问tppeof、GetType()、is、as的问题,在实验的时候顺手就用泛型写的例子。在看Jit后的反汇编时发现了一个问题,clr对泛型参数有些特殊处理。上网查了一下没有找到介绍泛型参数存储的文章因此动手做了一番实验,有了些浅显的理解在此记录下来望看到此文的高手能给予全面的解释。    以前用泛型的时候没想过clr是如何处理泛型参数的,今天查阅了下>里面讲解了一个叫做Ge

光辉的晨星 3861

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

C# 8 新提案让泛型 Attribute 成为现实

从一开始, Attribute 就是.NET 的一部分。因此,它们在引入泛型之前就已经被创建好,这意味着如果要引用属性中的类型,必须暴露一个 Type 参数或属性。例如:...

dotNET跨平台 633

.NET泛型03,泛型类型的转换,协变和逆变

协变(Convariant)和逆变(Contravariant)的出现,使数组、委托、泛型类型的隐式转换变得可能。 子类转换成基类,称之为协变;基类转换成子类,称之为逆变。.NET4.0以来,支持了泛型接口的协变和逆变。 泛型协变 如果子类泛型隐式转换成基类泛型,使用泛型协变。 有这样的2个基类和派生类。 public class Animal { publ...

weixin_30855099的博客 165

c# where(泛型类型约束)

c# where(泛型类型约束) 原文:c# where(泛型类型约束)定义:在定义泛型的时候,我们可以使用where限制参数的范围。 使用:在使用泛型的时候,你必须尊守where限制参数的范围,否则编译不会通过。 六种类型的约束: T:类(类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。) ...

weixin_30478923的博客 1942

C# 8新提案让泛型Attribute成为现实

从一开始, Attribute就是.NET的一部分。因此,它们在引入泛型之前就已经被创建好,这意味着如果要引用属性中的类型,必须暴露一个Type参数或属性。例如:[TypeConverter(typeof(X))]这种模式存在一些限制。在上面的示例中,X必须有一个无参数的构造函数,并继承TypeConverter。编译器不会强制这些,所以开发人员只能在运行时发现错误。相反,如果我们使用泛型类型参数...

weixin_33869377的博客 102
上一篇: 05-C#
下一篇: 02-C#.Net-反射-学习笔记
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值