C#进阶之泛型(Generic)
2024-09-04 21:22:55
1、泛型
泛型是framwork2.0推出的新语法,具有延迟声明的特点:把参数类型的声明推迟到调用的时候。泛型不是一个语法糖,是框架升级提供的功能。需要编辑器和JIT(just-in-time compilation、即时编译)的支持。
泛型并不存在性能问题,因为编译器支持 ,在即时编译的时候,编译器会生成一个泛型方法的副本,基本上和固定类型的方法性能无差别。
泛型的用处就是 用一个类、方法、接口、委托来满足不同的具体类型,然后做一样的事情。
泛型的约束有以下几种类型:
- 基类约束
- 接口约束
- 引用类型约束
- 值类型约束
- 无参数构造函数约束
约束必须是接口、非密封类(密封类无法被继承,不存在子类,所以约束没有意义)、类型参数;
约束可叠加,
泛型约束主要是用来保证代码安全。
2、协变逆变
这里用代码来解释一下这两个概念
using System;
using System.Collections.Generic; namespace Util_YCH.Build.泛型
{
/// <summary>
/// 协变实例
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IListAnimals<out T> {
/// <summary>
/// T只能作为返回值不能作为入参
/// </summary>
/// <returns></returns>
T GetT();
/// <summary>
/// 所以这里会编译报错
/// </summary>
/// <param name="t"></param>
void setT(T t);
}
public class ListAnimals<T> : IListAnimals<T>
{
T t;
public T GetT()
{
throw new NotImplementedException();
} public void setT(T t)
{ }
}
/// <summary>
/// 逆变实例
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IListDogs<in T>
{
/// <summary>
/// T只能作为入参不能作为返回值
/// </summary>
/// <param name="t"></param>
void setT(T t);
/// <summary>
/// T无法作为返回值,此处会编译报错
/// </summary>
/// <returns></returns>
T GetT(); }
public class ListDogs<T> : IListDogs<T>
{
public T GetT()
{
throw new NotImplementedException();
} public void setT(T t)
{
throw new NotImplementedException();
}
}
/// <summary>
/// 动物类
/// </summary>
public class Animal
{
}
/// <summary>
/// 狗类
/// </summary>
public class Dog : Animal
{
} public class Test {
public Test(){ Animal animal1 = new Animal();
Dog dog = new Dog();
Animal animal2 = new Dog();//狗是动物
List<Animal> animals = new List<Dog>();
//按照常规而言,Dog是Animal的子类,这样写应该是没有问题的,凡是编译器报错,
//原因是因为Dog是Animal的子类,但是List<Dog> 与 List<Animal> 之间不存在继承关系,
//于是为了消除这个BUG,就有了【协变】的概念
#region 协变
IListAnimals<Animal> listAnimals = new ListAnimals<Dog>();
#endregion
#region 逆变
IListDogs<Dog> listAnimals2 = new ListDogs<Animal>();
#endregion
} }
}
3、泛型缓存
由于CLR会针对不同的类型会生成一个副本,所以可以实现泛型的缓存,示例代码如下
using System; namespace Util_YCH.Build.泛型
{
/// <summary>
/// 每个不同的类型T都会生成一个副本,
/// 根据C#语言特性,静态字段和方法会在程序第一次运行的时候执行,缓存效率远远高于字典等缓存。
/// </summary>
public class Cache<T>
{
public Cache()
{
CacheStr = DateTime.Now.ToString();
}
private static string CacheStr = "";
public static string GetCacheStr() {
return CacheStr;
}
} public class Test {
public Test(){ #region 泛型缓存
var cache1 = new Cache<int>();
var cache2 = new Cache<string>();
var cache3 = new Cache<DateTime>();
var cache4 = new Cache<double>();
var cache5 = new Cache<bool>();
#endregion var cache11 = Cache<int>.GetCacheStr();
var cache12 = Cache<int>.GetCacheStr();
Console.WriteLine(cache11 == cache12);
Console.ReadKey();
} }
}
字典缓存是哈希结构的,读取缓存的时候需要进行查找,会消耗一定的资源;而泛型缓存的副本存在于内存里面,查找起来速度极快,但是有局限性,就是和类型相关,具有一定的限制。
这里的应用场景我能想到的就是可以针对每个实体缓存CRUD的Sql语句。
最新文章
- JAVA WEB WITH IDEA
- centos 6.5 升级内核 linux 3.12.17 (笔记 实测)
- android 在使用ViewAnimationUtils.createCircularReveal()无法兼容低版本的情况下,另行实现圆形scale动画
- JS中匿名函数$(function(){ })和(function(){})()的区别
- c/c++ qsort 函数的简单使用(1)
- 分析 ";End"; ";Unload Me"; ";Exit Sub"; 之间的区别与联系
- DB天气app冲刺二阶段第九天
- Java对象的序列化和反序列化[转]
- 使用Pull解析器生成XML文件和读取xml文件
- 利用gridview实现计时消费,有点复杂,谁有好的方法可以讨论一下...
- Android系统源码导入到eclipse
- NFS : device is busy
- 【android】环形进度条实现
- php socket 函数
- java编写service详细笔记 - centos7.2实战笔记(windows类似就不在重复了)
- poj1067
- TypeScript 照猫画虎
- Oracle下如何用rman备份到特定的sequence
- cf Double Happiness(判断是否为素数且为4k+1型)
- SQL Server的聚集索引和非聚集索引