C#泛型(Generic)

一.什么是泛型

泛型(Generic)是C#语言2.0、通用语言运行时(CLR)2.0、.NET Framework2.0推出来的新特性。

泛型为.NET框架引入类型参数(Type Parameters)的概念。类型参数使得设计类和方法时,不必确定一个或多个参具体数。

具体的参数类型可延迟到声明和使用时再确定。避免了运行时类型转换或装箱操作的代价和风险。

二.泛型的使用和对比

2.1.CommandMethod(普通方法)

 1         /// <summary>
 2         /// 打印一个Int值
 3         ///
 4         /// 因为方法声明的时候,写死了参数类型
 5         /// </summary>
 6         /// <param name="iParameter"></param>
 7          public static void ShowInt(int iParameter)
 8         {
 9             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, iParameter.GetType().Name, iParameter);
10
11         }
12         /// <summary>
13         /// 打印一个string值
14         /// </summary>
15         /// <param name="sParameter"></param>
16         public static void ShowString(string sParameter)
17         {
18             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, sParameter.GetType().Name, sParameter);
19         }
20         /// <summary>
21         /// 打印一个DateTime值
22         /// </summary>
23         /// <param name="dtParameter"></param>
24         public static void ShowDateTime(DateTime dtParameter)
25         {
26             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, dtParameter.GetType().Name, dtParameter);
27         }

2.2.ObjectMethod(基类方法)

 1         /// <summary>
 2         /// 打印一个object值
 3         ///
 4         /// object引用类型 假如传个值类型  会有装箱拆箱  性能损失
 5         ///  类型不安全
 6         /// </summary>
 7         /// <param name="oParameter"></param>
 8         public static void ShowObject (object oParameter)
 9         {
10             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, oParameter.GetType().Name, oParameter);
11
12             //Console.WriteLine($"{((People)oParameter).Id}_{((People)oParameter).Name}");
13         }

2.3.GenericMethod(泛型方法)

 1         /// <summary>
 2         /// 2.0推出的新语法
 3         /// 一个方法满足不同参数类型 做相同的事
 4         ///
 5         /// 延迟声明:把参数类型的声明推迟到调用
 6         /// 不是语法糖,而是由框架升级提供的功能
 7         ///
 8         /// 没有写死参数类型,调用的时候才指定的类型
 9         /// </summary>
10         /// <typeparam name="T">T/S 不要用关键字 也不要跟别的类型冲突</typeparam>
11         /// <param name="tParameter"></param>
12         public static void Show<T>(T tParameter)
13         {
14             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
15         }

2.3调用

 1                 int iValue = 123;
 2                 string sValue = "456";
 3                 DateTime dtValue = DateTime.Now;
 4                 object oValue = "789";
 5                 Console.WriteLine("*********普通方法************");
 6                 CommonMethod.ShowInt(iValue);
 7                 CommonMethod.ShowString(sValue);
 8                 CommonMethod.ShowDateTime(dtValue);
 9                 CommonMethod.ShowObject(oValue);
10
11                 Console.WriteLine("*********通过Object************");//.NET Framework1.0 1.1
12                 /*为什么可以传string、int、datetime、class等的原因
13                   1. object类型是一切类型的父类
14                   2. 通过继承,子类拥有父类的一切属性和行为;任何父类出现的地方,都可以用子类来代替
15                 */
16                 CommonMethod.ShowObject(iValue);
17                 CommonMethod.ShowObject(sValue);
18                 CommonMethod.ShowObject(dtValue);
19
20                 Console.WriteLine("***********通过泛型***************");
21                 GenericMethod.Show<int>(iValue);//需要指定类型参数
22                 GenericMethod.Show<string>(sValue);//必须吻合
23                 GenericMethod.Show<DateTime>(dtValue);//能省略自动推算
24                 GenericMethod.Show<object>(oValue);

2.4.性能对比

  1     /// <summary>
  2     /// 性能监视类
  3     /// </summary>
  4    public class Monitor
  5     {
  6         public static void Show()
  7         {
  8             int iValue = 12345;
  9             long commonSecond = 0;
 10             long objectSecond = 0;
 11             long genericSecond = 0;
 12
 13             {
 14                 //提供一组方法和属性,可用于准确地测量运行时间
 15                 Stopwatch stopwatch = new Stopwatch();
 16                 //开始或继续测量某个时间间隔的运行时间。
 17                 stopwatch.Start();
 18                 for (int i = 0; i < 100000000; i++)
 19                 {
 20                    ShowInt(iValue);
 21                 }
 22                 // 停止测量某个时间间隔的运行时间。
 23                 stopwatch.Stop();
 24                //获取当前实例测量得出的总运行时间(以毫秒为单位)。
 25                 commonSecond = stopwatch.ElapsedMilliseconds;
 26             }
 27             {
 28                 Stopwatch stopwatch = new Stopwatch();
 29                 stopwatch.Start();
 30                 for (int i = 0; i < 100000000; i++)
 31                 {
 32                     ShowObject(iValue);
 33                 }
 34                 stopwatch.Stop();
 35                 objectSecond = stopwatch.ElapsedMilliseconds;
 36             }
 37             {
 38                 Stopwatch stopwatch = new Stopwatch();
 39                 stopwatch.Start();
 40                 for (int i = 0; i < 100000000; i++)
 41                 {
 42                     Show(iValue);
 43                 }
 44                 stopwatch.Stop();
 45                 genericSecond = stopwatch.ElapsedMilliseconds;
 46             }
 47
 48             Console.WriteLine($"普通方法耗时:{commonSecond} Object方法耗时:{objectSecond} Generic方法耗时:{genericSecond}");
 49         }
 50
 51         private static void ShowInt(int iParameter)
 52         {
 53
 54         }
 55
 56         private static void ShowObject(object oParameter)
 57         {
 58
 59         }
 60
 61         private static void Show<T>(T tParameter)
 62         {
 63
 64         }
 65
 66         #region Stopwatch详解
 67         /*
 68              获取以每秒计时周期数表示的计时器频率。此字段为只读。
 69         public static readonly long Frequency;
 70              指示计时器是否基于高分辨率性能计数器。此字段为只读。
 71         public static readonly bool IsHighResolution;
 72              初始化 System.Diagnostics.Stopwatch 类的新实例。
 73         public Stopwatch();
 74              获取当前实例测量得出的总运行时间。
 75            返回结果:
 76              一个只读的 System.TimeSpan,用于表示当前实例测量得出的总运行时间。
 77         public TimeSpan Elapsed { get; }
 78              获取当前实例测量得出的总运行时间(以毫秒为单位)。
 79            返回结果:
 80              一个只读长整型,表示当前实例测量得出的总毫秒数。
 81         public long ElapsedMilliseconds { get; }
 82              获取当前实例测量得出的总运行时间(用计时器计时周期表示)。
 83            返回结果:
 84              一个只读长整型,表示当前实例测量得出的计时器计时周期的总数。
 85         public long ElapsedTicks { get; }
 86              获取一个指示 System.Diagnostics.Stopwatch 计时器是否在运行的值。
 87            返回结果:
 88              如果 System.Diagnostics.Stopwatch 实例当前正在运行,并且在对某个时间间隔的运行时间进行测量,则该值为 true;否则为 false。
 89         public bool IsRunning { get; }
 90              获取计时器机制中的当前最小时间单位数。
 91            返回结果:
 92              一个长整型,表示基础计时器机制中的计时周期计数器值。
 93         public static long GetTimestamp();
 94              对新的 System.Diagnostics.Stopwatch 实例进行初始化,将运行时间属性设置为零,然后开始测量运行时间。
 95            返回结果:
 96              刚刚开始测量运行时间的 System.Diagnostics.Stopwatch。
 97         public static Stopwatch StartNew();
 98              停止时间间隔测量,并将运行时间重置为零。
 99         public void Reset();
100              停止时间间隔测量,将运行时间重置为零,然后开始测量运行时间。
101         public void Restart();
102              开始或继续测量某个时间间隔的运行时间。
103         public void Start();
104             停止测量某个时间间隔的运行时间。
105         public void Stop();
106        */
107         #endregion
108     }
109 }

2.6对比结果

三.泛型的原理

泛型在编译的时候,类型是不明确的,类型参数会被系统编译成占位符 (~),在运行时,Jit即时编译会根据程序中调用泛型方法时候给它指定的类型参数替换过来

四. 泛型类、泛型方法、泛型接口、泛型委托

 1     /// <summary>
 2     /// 泛型类
 3     /// 一个类来满足不同的具体类型,来做相同的事
 4     /// </summary>
 5     public class GenericClass<T>
 6     {
 7
 8     }
 9
10     public class GenericClass<T,S> where T:People where S:Hunan
11     {
12
13     }
14
15     /// <summary>
16     /// 泛型接口
17     /// 一个接口来满足不同的具体类型的接口,来做相同的事
18     /// </summary>
19     public interface IGenericInterface<T> //where T:People
20     {
21
22     }
23
24     public class CommonClass:GenericClass<int>//使用泛型必须指定类型
25     {
26
27     }
28
29     public class GenericClassChild<E>:GenericClass<E>
30     {
31
32     }
33
34     /// <summary>
35     /// 泛型委托
36     /// </summary>
37     /// <typeparam name="T"></typeparam>
38     public delegate void GenericDelegate<T>();

五.泛型约束

泛型定义中的 where 子句指定对用作泛型类型、方法、委托或本地函数中类型参数的参数类型的约束。

约束可指定接口、基类或要求泛型类型为引用、值或非托管类型。 它们声明类型参数必须具备的功能。

 1     /// <summary>
 2     /// 约束类
 3     /// 泛型:不同的参数类型都能进来;任何类型都来进来,无法准确定位
 4     /// 没有约束,也就没有自由
 5     /// 泛型约束-----基类约束(不能是sealed)
 6     /// 1.可以使用基类的一切属性方法---权利
 7     /// 2.强制保证T一定是People或者People的子类
 8     ///
 9     ///
10     /// 为什么不能用int(Int32)?
11     ///“int”不是有效的约束。作为约束使用的类型必须是接口、非密封类或类型参数 而Int32是密封类(sealed)
12     /// </summary>
13     public class Constraint
14     {
15         public static void Show<T>(T tParameter)
16             where T : People,ISports,IWork,new()//基类约束
17         {
18             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
19
20             Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
21             tParameter.Hi();
22             tParameter.Pingpang();
23             tParameter.Work();
24         }
25
26         /// <summary>
27         /// 为什么不用基类
28         /// </summary>
29         /// <param name="tParameter"></param>
30         public static void ShowBase(People tParameter)//因为约束可以叠加 更灵活
31         {
32             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
33             Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
34             tParameter.Hi();
35         }
36
37         public static T InterfaceGet<T>(T t)
38             where T : ISports  //接口约束
39         {
40             t.Pingpang();
41             return t;
42         }
43
44         public static T ClassGet<T>(T t)
45           where T : class  //引用类型约束:保证引用类型
46         {
47             T tNew = null;
48             return t;
49         }
50
51         public static T StructGet<T>(T t)
52             where T : struct //值类型约束
53         {
54             T tNew = default(T); //会根据T的不同 赋予默认值
55             return t;
56         }
57
58         public static T NewGet<T>(T t)
59          where T : new() //无参数构造函数约束
60         {
61             T tNew = new T();
62             return t;
63         }
64     }

六.协变和逆变

可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量

协变和逆变是两个相互对立的概念:

  • 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变
  • 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变
  1    /// <summary>
  2     ///  .NET 4.0出现的
  3     /// 只能放在接口或者委托的泛型参数前面
  4     /// out 协变covariant 修饰返回值
  5     /// in 逆变contravariant 修饰传入参数
  6     /// </summary>
  7     public class CCTest
  8     {
  9         public static void Show()
 10         {
 11             {
 12                 Bird bird1 = new Bird();
 13                 Bird bird2 = new Sparrow();
 14                 Sparrow sparrow = new Sparrow();
 15                 //Sparrow sparrow=new Bird();//不合理 鸟不一定是麻雀
 16             }
 17
 18             {
 19                 List<Bird> birdList1 = new List<Bird>();
 20                 //两个不同的类型 没有父子关系
 21                 //List<Bird> birdList2 = new List<Sparrow>();//应该可以 一堆麻雀是一堆鸟
 22                 //在.NET Core中需要引用System.Linq;
 23                 List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList();
 24             }
 25
 26             {
 27                 //协变
 28                 IEnumerable<Bird> birdList1 = new List<Bird>();//接口
 29                 IEnumerable<Bird> birdList2 = new List<Sparrow>();
 30                 Func<Bird> func = new Func<Sparrow>(() => null);//委托
 31                 //自定义
 32                 ICustomerListOut<Bird> customerList1 = new CustomerListOut<Bird>();
 33                 ICustomerListOut<Bird> customerList2 = new CustomerListOut<Bird>();
 34             }
 35
 36             {
 37                 //逆变
 38                 ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Sparrow>();
 39                 ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Bird>();
 40                 ICustomerListIn<Bird> customerList3 = new CustomerListIn<Bird>();
 41                 customerList3.Show(new Bird());
 42                 customerList3.Show(new Sparrow());
 43                 Action<Sparrow> action = new Action<Bird>((Bird i) => { });
 44             }
 45
 46             {
 47                 IMyList<Sparrow, Bird> myList1 = new MyList<Sparrow, Bird>();
 48                 IMyList<Sparrow, Bird> myList2 = new MyList<Sparrow, Sparrow>();//协变
 49                 IMyList<Sparrow, Bird> myList3 = new MyList<Bird, Bird>();//逆变
 50                 IMyList<Sparrow, Bird> myList4 = new MyList<Bird, Sparrow>();//逆变+协变
 51             }
 52         }
 53     }
 54
 55     /// <summary>
 56     /// 鸟类
 57     /// </summary>
 58     public class Bird
 59     {
 60         public int Id { get; set; }
 61     }
 62     /// <summary>
 63     /// 麻雀类
 64     /// </summary>
 65     public class Sparrow : Bird
 66     {
 67         public string Name { get; set; }
 68     }
 69     /// <summary>
 70     /// in 逆变 只能做参数
 71     /// </summary>
 72     /// <typeparam name="T"></typeparam>
 73     public interface ICustomerListIn<in T>
 74     {
 75         void Show(T t);
 76
 77         //T Get(); 逆变,不能作为返回值,只能把它当成参数
 78     }
 79
 80     public class CustomerListIn<T> : ICustomerListIn<T>
 81     {
 82         public void Show(T t)
 83         {
 84
 85         }
 86
 87         //public T Get()
 88         //{
 89         //    return default(T);
 90         //}
 91     }
 92     /// <summary>
 93     /// out 协变 只能是返回结果
 94     /// </summary>
 95     /// <typeparam name="T"></typeparam>
 96     public interface ICustomerListOut<out T>
 97     {
 98         T Get();
 99
100         //void Show(T t); 只能放在返回值,不能放在参数
101     }
102
103     public class CustomerListOut<T> : ICustomerListOut<T>
104     {
105         public T Get()
106         {
107             return default(T);
108         }
109
110         //public void Show(T t)
111         //{
112
113         //}
114     }
115
116     public interface IMyList<in inT,out outT>
117     {
118         void Show(inT t);
119
120         outT Get();
121
122         outT Do(inT t);
123
124        //out 只能是返回值 in只能是参数
125
126     }
127
128     public class MyList<T1, T2> : IMyList<T1, T2>
129     {
130         public T2 Do(T1 t)
131         {
132             Console.WriteLine(t.GetType().Name);
133             Console.WriteLine(typeof(T2).Name);
134             return default(T2);
135         }
136
137         public T2 Get()
138         {
139             Console.WriteLine(typeof(T2).Name);
140             return default(T2);
141         }
142
143         public void Show(T1 t)
144         {
145             Console.WriteLine(t.GetType().Name);
146         }
147     }

七.泛型缓存

 1     public class GenericCacheTest
 2     {
 3         public static void Show()
 4         {
 5             for (int i = 0; i < 5; i++)
 6             {
 7                 Console.WriteLine(GenericCache<int>.GetCache());
 8                 Thread.Sleep(10);
 9                 Console.WriteLine(GenericCache<long>.GetCache());
10                 Thread.Sleep(10);
11                 Console.WriteLine(GenericCache<DateTime>.GetCache());
12                 Thread.Sleep(10);
13                 Console.WriteLine(GenericCache<string>.GetCache());
14                 Thread.Sleep(10);
15                 Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
16                 Thread.Sleep(10);
17             }
18         }
19     }
20     /// <summary>
21     /// 字典缓存:静态属性常驻内存
22     /// </summary>
23     public class DictionaryCache
24     {
25         private static Dictionary<Type, string> _TypeTimeDictionary = null;
26
27         static DictionaryCache()
28         {
29             Console.WriteLine("This is Dictionary 静态构造函数");
30             _TypeTimeDictionary = new Dictionary<Type, string>();
31         }
32
33         public static string GetCache<T>()
34         {
35             Type type = typeof(Type);
36             if (!_TypeTimeDictionary.ContainsKey(type))
37             {
38                 _TypeTimeDictionary[type] = string.Format("{0}_{1}",typeof(T).FullName,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
39             }
40             return _TypeTimeDictionary[type];
41         }
42     }
43     /// <summary>
44     /// 每个不同的T,都会生成一份不同的副本
45     /// 适合不同类型,需要缓存一份数据的场景,效率高
46     /// 不能主动释放
47     /// </summary>
48     /// <typeparam name="T"></typeparam>
49     public class GenericCache<T>
50     {
51         private static string _TypeTime = "";
52         static GenericCache()
53         {
54             Console.WriteLine("This is GenericCache 静态构造函数");
55             _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
56         }
57
58         public static string GetCache()
59         {
60             return _TypeTime;
61         }
62     }
63     /*
64      * 为什么不用字典缓存?
65      * 字典缓存是一种哈希分布的,找数据的时候需要把Key进行哈希 找到内存地址 然后才能找到数据 数据如果过多 范围就会很大 造成性能浪费
66      * 而泛型缓存,每一个都是独立的 不同的实体产生一个不同的副本,副本就存在CPU那,已经存在即时编译器里面去,如果需要找不同的类 直接在内存拿、寻址
67      * 而字典缓存需要去计算去寻址
68      *
69      * 泛型生命周期:永远都不释放
70      */

八.泛型缓存

原文地址:https://www.cnblogs.com/vic-tory/p/12148153.html

时间: 2024-10-09 08:50:21

C#泛型(Generic)的相关文章

Java基础之Comparable接口, Collections类,Iterator接口,泛型(Generic)

一.Comparable接口, Collections类 List的常用算法: sort(List); 排序,如果需要对自定义的类进行排序, 那就必须要让其实现Comparable接口, 实现比较两个类大小的方法 shuffle(List); 随机排列 void reverse(List); 逆序排列(Linked效率较高) copy(); 复制集合, 前提是size()相同(长度, 和容量的区别) fill(List, Object);使用某个对象填充整个List binarySearch()

JAVA学习笔记(五十六)- 泛型 Generic Types

泛型 Generic Types import java.util.ArrayList; import java.util.List; /* * 泛型 Generic Types * 集合泛型 * 类泛型 * 方法泛型 */ public class Test01 { public static void main(String[] args) { // 1.集合泛型,保证集合中元素的类型安全 List<Integer> nums = new ArrayList<Integer>(

Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口

Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器内的元素的遍历 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭代器通常被称为"轻量级"对象,因为创建它的代价小. Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个I

泛型 Generic 类型擦除引起的问题及解决方法

参考:http://blog.csdn.net/lonelyroamer/article/details/7868820#comments 因为种种原因,Java不能实现真正的泛型,只能使用类型擦除来实现伪泛型,这样虽然不会有类型膨胀的问题,但是也引起了许多新的问题.所以,Sun对这些问题作出了许多限制,避免我们犯各种错误. 1.先检查,再编译,以及检查编译的对象和引用传递的问题 既然说类型变量会在编译的时候擦除掉,那为什么我们往ArrayList<String> arrayList=new

java 泛型(Generic)

java泛型(generic) 2016-07-11 问题 向集合存储元素存在安全性问题,即很可能不知道已有的集合中已经存储的元素的类型是什么,这时就不应该向该集合随便添加元素,否则与集合的设计的初衷相悖,因为集合存储的是同类型的元素,而且以后取集合中的元素不知道该转为什么类型. 从集合中获取元素需要强制类型转换,可能出现ClassCastException问题 未使用泛型举例: 说明:任何Object子类均可以存入Object集合,但是从集合中获取元素,则是Object对象,需要强制转换,可能

泛型(Generic)

1.定义:一种特殊的变量,保存的是引用变量的类型 2.好处:避免数据类型的转换;将运行时期ClassCastException提前到编译时期 3.自定义带泛型的类:     public class A<泛型名>{ }     注:类中凡是使用到数据类型的地方,都可以使用泛型名代替;         泛型名自定义,但是将来使用者只能传递引用数据类型; 1 代码实现: 2 //自定义泛型类和泛型方法: 3 class A<MM>{ 4 private MM name; 5 priva

1、(知识篇)泛型Generic

泛型的使用: public class Test { public static void main(String[] args) { DoWork<Person> doWork = new DoWork<>(); doWork.doSth(new Person()); DoEat<Man> doEat = new DoEat<>(); doEat.doSth(new Man()); } // 普通类 public static class Person {

Java泛型Generic - Thinking in Java读书笔记

1.什么是泛型? 泛型就是可适用于很多的类,它实现了参数化类型type parameter的概念,参数化类型指可使用多种类型. 多态是一种泛化机制. 例如方法 A(Base b){}//Base是一个基类 这样方法A就可以接受从这个基类Base的任何子类作为参数.方法的参数也可以是一个接口,但是由于类的单继承体系和接口的约束( An interface still requires that your code works with that particular interface. ),不能

1.泛型(Generic)

一.泛型 泛型就是封装,将重复的工作简单化 1.泛型方法 public static void Show<T>(T tParameter) { Console.WriteLine("This is {0}, parameter = {1}, type = {2}", typeof(CommonMethod).Name, tParameter.GetType(), tParameter); } 2.泛型类 public class GenericClass<T>{