泛型:运行原理

1,泛型没有写死类型,调用的时候指定类型,这个是延时声明
2,延时声明,把参数类型的声明推迟到调用,
3,在即时编译中将泛型代码生成了原生代码,根据不同的类型生成不同的副本,等待程序去运行,性能跟原生代码几乎一致,

一,泛型有泛型类,泛型方法,泛型委托和泛型接口

泛型类 :这个命名空间的namespace System.Collections.Generic的泛型类,GenericDemo<T>等

泛型方法:泛型中有where ,Select等泛型的扩展方法,GetT<T>等

public  void GetT<T>(T t)
{Console.WriteLine(t);
}

泛型接口:IEnumerable这个是泛型接口

public interface GenericInter<T>
{}

泛型委托:func和Action

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GenericDelegate
{class Program{static void Main(string[] args){People p = new People();//定义了一个委托,这个委托带参数p.show("abc", p.Say);p.show(123, p.Say);}}public class People{public delegate void showDelegate<T>(T t);public void show<T>(T t, showDelegate<T> showDelegate){showDelegate(t);}public void Say<T>(T t){Console.WriteLine(t);}}
}

二,为什么要有泛型?

多个方法,但是参数不一样时候,我们需要用泛型来解决,用object会造成太多装箱拆箱,消耗性能 ,如下公用代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GenericDemo
{class Program{static void Main(string[] args){show(1);show("test");Console.Read();}public static void show<T>(T t) {Console.WriteLine(t);}}
}

二,什么是泛型缓存?先看如下,代码运行结果

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GenericDemo
{class Program{static void Main(string[] args){var s = new SqlUtil<User>();var str1 = s.GetSql();Console.WriteLine(str1);var s1 = new SqlUtil<User>();var str2 = s1.GetSql();Console.WriteLine(str2);//一开始var bo = s.Equals(s1); //输出false,证明两个构造出来的变量是不一样的
            Console.WriteLine(bo);}}public class User{public int Id { get; set; }public string Name { get; set; }public string Title { get; set; }}
    public class Dept{public int Id { get; set; }public string Name { get; set; }public string Title { get; set; }}
 }

SqlUtil.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GenericDemo
{public class SqlUtil<T>{public static string strSql = "";//public SqlUtil()//{//    Console.WriteLine("{0}public构造",this.GetType().Name);//}static SqlUtil(){Type t = typeof(T);var colums = string.Join(",", t.GetProperties().Select(m => $"[{m.Name}]"));strSql = $"select {colums} from {t.Name}";Console.WriteLine("{0}被构造", t.Name);}public string GetSql(){return strSql;}}
}

运行结果如下

这个时候我们就好奇了,为什么user被构造只是出现一次?那这样会不会理解成静态构造只是构造一个实例?其实s和s1是相等的?我们用代码比较一下

var bo = s.Equals(s1); //输出false,证明两个构造出来的变量是不一样的
Console.WriteLine(bo);

可结果出乎我意料,结果他们两个实例是不相等的。那这个是什么原因呢?我们给SqlUtil添加构造函数

public SqlUtil()
{Console.WriteLine("{0}public构造",this.GetType().Name);
}

结果输出了,SqlUtil被构造了两次,被初始化了两次结果肯定是不一样的,所以s和s1的值肯定不相等。那这个时候我们就疑惑了。静态构造的意义在哪里?根据上图结果,静态构造在特定类型的整个进程中,只会被构造一次,那他跟泛型缓存又有什么关系呢?

三,我们先来理解下程序编译整一个过程,如下图:

根据上图,我们可以总结为:

一,如下

1,程序在vs等开发工具中编译成DLL/ EXE,然后经过CLR(通用语言进行时)和JIT即时编译(二次编译),然后才能被转换为计算机语言,(PS:CLR和JIT是window安装了.netframework就有的的运行环境)

2,在编译器编译成DLL后,其中有metadata和IL,IL是被CLR和JIT编译成机器码的中间语言,而metadata是描述你的dll的,被反射后你能看得懂的代码

我们用ILspy反射下,看下图:c#这个是metadata存的给我们反射看的懂的,而IL模式这是给二次编译成机器码的中间语言

看下IL的代码

二,那泛型的编译过程是怎样的呢?

1, 程序编译泛型时会生成一个带占位符中间语言,在中间语言的时候泛型的类型是不确定的,占位符和替换结果如下代码:

Console.WriteLine(typeof(List<>));
Console.WriteLine(typeof(List<string>));

2,在CLR和JIT的二次编译成机器码的时候,方法可以确定被调用,这个时候就可以确定泛型的类型,将真实类型替换占位符,生成确定类型的代码,代码执行的性能跟原声的代码差不多

三,那泛型缓存是怎样的呢?

1,JIT会为不同类型生成一个代码副本,所以泛型类List<int>和List<string>产生不同的代码副本,而当调用List<string>两次时使用的是同一个代码副本,所以泛型类在指定不同的类型参数后就是一个独立的类

2,静态构造函数只会执行一次,在任何一个类里面静态构造函数只会在第一被代码调用当前类调用执行调用,并且只会调用一次,而根据泛型的不同类型就是不同类的定义所以一下代码静态构造会被执行两次

var s3 = new SqlUtil<User>();
var s31 = new SqlUtil<User>();
var s4 = new SqlUtil<Dept>();

3,总结:泛型缓存是利用泛型的编译原理,不同的类型参数产生不同的类,类里面用静态缓存数据,下次可以直接用静态字段的数据(PS:即是静态构造根据泛型的类型不一样,会被执行一次,执行静态构造是泛型类中的静态字段同时赋值,下次调用相同类型的泛型类时,可以直接使用静态字段保存的值,是利用泛型的编译原理和静态字段来缓存数据

4,泛型缓存是根据类型相关,一个类型只能缓存一个结果,一份数据,相对字典来说性能高,字典是(key-value的hash存储,每次查找都要进行哈希运算),泛型缓存则是直接被JIT编译成一个代码段,这个代码段又有静态缓存,可以直接用,所以性能比字典高

5,泛型缓存:泛型会在JIT编译中,为不同类型生成不同的副本,那么在不同副本中,静态元素是独立的,初始使用会去初始化,以后可以直接重用,这就是缓存的效果

转载于:https://www.cnblogs.com/May-day/p/10872320.html

什么是泛型缓存和静态构造函数?相关推荐

  1. 第五节:泛型(泛型类、接口、方法、委托、泛型约束、泛型缓存、逆变和协变)

    一. 泛型诞生的背景 在介绍背景之前,先来看一个案例,要求:分别输出实体model1.model2.model3的id和name值,这三个实体有相同的属性名字id和name. 1 public cla ...

  2. [转载]静态构造函数

    静态构造函数的理解     class Program     {         static void Main(string[] args)         {             Cacu ...

  3. Effective C# 原则13:用静态构造函数初始化类的静态成员(译)

    Effective C# 原则13:用静态构造函数初始化类的静态成员 Item 13: Initialize Static Class Members with Static Constructors ...

  4. 由单例模式学到:静态构造函数和静态字段

    编译器在编译的时候,会事先分析所需要的静态字段,如果这些静态字段所在的类有静态的构造函数,则忽略静态字段的初始化,否则先进行静态字段的初始化.对类的静态成员初始化的顺序取决于在Main函数中的引用顺序 ...

  5. WIN7 UAC/结构体的Equals方法/C# 开发wince程序,窗口上总留有一块空白区域/静态构造函数...

    1. vs2005在win7下通过IIS7调试时,调用dll失败,但在XP下正常.相信是权限问题,请问应该如何解决?能不能设置dll默认以管理员身分运行? 备注:此DLL已注册成功! 回复引用 全部回 ...

  6. 子类如果不实例化则不会调用它自身的静态构造函数

    using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Conso ...

  7. C#构造函数、私有构造函数、静态构造函数与构造函数执行顺序

    https://www.cnblogs.com/ArtofDesign/p/3603986.html 默认构造函数,如果没有为类指定任何构造函数,编译器会自动为类创建一个无参构造函数,用以初始化类的字 ...

  8. C#静态构造函数总结

    今天花了一些时间把静态构造函数的用法总结了一下,希望高手们指点.谢谢! 静态构造函数既没有访问修饰符,也没有参数. 如果没有编写静态构造函数,而这时类中包含带有初始值设定的静态字段,那么编译器会自动生 ...

  9. c#中的静态构造函数

    静态构造函数是C#的一个新特性,其实好像很少用到.不过当我们想初始化一些静态变量的时候就需要用到它了.这个构造函数是属于类的,而不是属于哪里实例的,就是说这个构造函数只会被执行一次.也就是在创建第一个 ...

最新文章

  1. oracle ORA-01113的解决方法(file 1 needs media recovery)
  2. Spring Cloud Netflix Zuul中的速率限制
  3. 数组取10个元素_不知道取什么样的英文名,看看老外最喜欢取的10个男孩和女孩名...
  4. 概率编程库Pymc3案例之线性回归
  5. 使用Container.ItemIndex获取Repeater、Gridview行的序号的简单方法
  6. Android支持横行滚动的ListView控件
  7. startActivityForResult用法详解
  8. 【CodeForces - 546C 】Soldier and Cards (模拟)
  9. 移动数据网络类型是nr_便携式移动网络的快速搭建方法
  10. mnist数据集svm python_python支持向量机分类MNIST数据集
  11. content:\2b 是什么意义
  12. at24c16如何划分出多个读写区_AVR学习笔记九、基于AT24C16的数据存储实验
  13. Linux-MySQL主从配置
  14. 【地理信息技术】 上机02 制作上海市行政区划图
  15. springboot通过maven管理mysql驱动加载失败
  16. 【java】java如何保留时分秒存取mySql中的datetime类型数据的方法 及 显示时间与数据库存放时间相差n小时的解决方法
  17. python完成非线性拟合
  18. 电脑连WiFi怎么查看密码是多少
  19. (二十七) 开运算、闭运算、形态梯度、顶帽、黑帽
  20. Redis三主三从集群搭建(三台机器)

热门文章

  1. C 语言——字符串和格式化输入/输出
  2. 一致性Hash算法(KetamaHash)的c#实现
  3. logic:equal 标签的使用(转)
  4. WCF方法拦截及OperationInvoker传递参数到WCF方法的实现
  5. 批量按照文件大小排序并修改文件名
  6. ntbackup 创建卷影复制:ffffffff 时出现错误
  7. SSH协议、HTTPS中SSL协议的完整交互过程
  8. suse 12sp1 oracle 11g r2 时出现错误 调用/sysman/lib/ins_emagent.mk的目标nmo时出错
  9. 2月07日云栖精选夜读:观点 | 阿里云 MVP 唐俊飞:安全性可以认为是一种能力...
  10. Office 2016 for Mac 15.24已推送至Office Insider慢速更新通道