为什么需要泛型,出现背景:
代码重用,好处:
源代码保护,类型安全,更清晰的代码,更佳的性能
FCL(框架类型,相当于java中jar包)中应用最广泛的地方,就是泛型集合类,
第一个知识点:
开放类型与封闭类型,
具有泛型参数的类型称为开放类型,
为所有参数类型传递实际数据类型的类型称为封闭类型
理解:即可以赋值具体数据类型,所以称为开放类型,
不管是封闭类型,还是开放类型,本质上仍然是类型,
也有类型对象,type object,区别是,CLR允许构造封闭类型的实例,
不允许构造开放类型实例,类似的CLR禁止构造接口实例,
是一回事儿
demo:
internal sealed class DictionaryNew
}
type t=Tvalue.getType("Dictionary<>");
Activitor.CreateInstance();
报错
第二个知识点:泛型继承
泛型既然也是一种类型,同样符合继承条件
internal class Node{
protected Node m_next;
public Node(Node node){
m_next=next;
}
}
internal sealed class NodeType
{
public T t_data;
//必须继承base(next)
public NodeType(T data,Node n):base(next)
{
t_data=data;
}
public NodeType(T data):this(data,null)
{
}
//重点是这个
public override ToString(){
//
string res=string.Empty;
if(m_next!=null){
//将后赋值添加内容,放在后面,
//m_next内容放在前面
res=m.next.ToString()+t_data;
//很巧妙自己调用自己
}
else{
res=t_data;
}
}
第三个知识点:泛型同一性
即可声明
List
简化<>优化阅读方式:
internal sealed class listdatetime:List
接下来直接使用
listdate ld=new listdate();
官方建议做法:
using listdate=System.Collections.Generic.List
第四个知识点:代码爆炸,泛型缓存
主要解决问题,针对泛型实参为值类型,生成不同本机代码
demo
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
public class TestTCache
{
static void Main(string[] args)
{
CjavapyCacheTest.Show();
}
public class CjavapyCacheTest
{
public static void Show()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(GenericCache.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache.GetCache());
Thread.Sleep(10);
}
}
}
///
/// 每个不同的T,都会生成一份不同的副本,
/// 理解相当于调用几个类型,class GenericCacheInt,class GenericCacheLong,..
/// 适合不同类型,需要缓存一份数据的场景,效率高
/// 不能主动释放
///
///
public class GenericCache
{
static GenericCache()
{
Console.WriteLine("This is GenericCache 静态构造函数");
_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
}
private static string _TypeTime = "";
public static string GetCache()
{
return _TypeTime;
}
}
///
/// 字典缓存:静态属性常驻内存
///
public class DictionaryCache
{
private static Dictionary _TypeTimeDictionary = null;
static DictionaryCache()
{
Console.WriteLine("This is DictionaryCache 静态构造函数");
_TypeTimeDictionary = new Dictionary();
}
public static string GetCache()
{
Type type = typeof(T);
if (!_TypeTimeDictionary.ContainsKey(type))
{
_TypeTimeDictionary[type] = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
}
return _TypeTimeDictionary[type];
}
}
}
}
这个例子也是泛型缓存的例子,可是项目实际中如何应用开发,博主也没遇到,欢迎同行留言私信交流
第五个知识点:不仅仅是类可声明泛型类型,
委托和接口也都可以声明泛型参数,称为泛型委托类型,或是泛型接口类型
委托本质上只是提供四个方法的一个类,
public delegate TReturn CallMe
第五个知识点:委托或接口的逆变,协变泛型类型实参
逆变是针对输入的,逆变实参可接收派生类,比如方法参数
它用关键字in标记,
协变是针对输出,比如方法返回,可返回基类,
它用关键字out标记
public delegate TResult Func
Func
//接收一个string输入,它是object派生
//返回一个基类是可以的
Func
第六个知识点:泛型方法
不仅仅是在类,接口,委托之中定义泛型参数
方法中同样可以定义泛型参数,
//类中接收的一个泛型实参,转为方法中的一个泛型实参
public sealed class GenericType
public T t;
//构造器给属性赋值
public GenericType(T tt){
t=tt;
}
public TOutput Convertor
object o;
TOutput tt=(TOutput)Convert.ChangeType(t,typeof(TOutput));
return tt;
}
}
public static void Swap
T temp;
temp = t1;
t1 = t2;
t2 = temp;
}
//可自己推断类型
第七个知识点:
在c#中,属性,索引器,事件,操作符方法,构造器,终结器
不能有泛型参数,但是能在泛型类型中进行定义,并且可使用泛型类型的的参数
第七个知识点:
泛型参数,本身可进行各种约束关键字where
public static T Min
//提示没有
if (t1.CompareTo(t2)<0)
{
return t1;
}
return t2;
}
//T 不是进行接口约束,报错
类型,方法,不能基于约束,和参数名称进行重载
即
internal sealed class AanyType();
internal sealed class AanyType
internal sealed class AanyType
类型参数可指定零个或一个主要约束,
internal sealed class Primary
public void Me(T stream){
}
}
有两个特殊的主要约束:
class引用类型,类类型,接口类型,委托类型,数组类型
struct值类型
次要约束:
代表接口类型
类型参数约束,也是裸约束
接收一个list返回另一个list
//方法声明两个T
public List
{}
构造函数约束,:new()
定义必须有无参构造函数
其他知识点:
泛型类型转型,不能直接转型,必须转为object
默认值default(T)
public void Test
T t=default(T);
int x=(int)(object)t;
}
泛型目前存在的问题,在于基元值类型,
无法引用加减乘除都操作数,即便是约束泛型参数为值类型
public T sum
T temp=defaulst(T);
for(var i=0;i
}
return temp;
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace WaitForPendingFinalizersExample
{
class MyWaitForPendingFinalizersClass
{
// You can increase this number to fill up more memory.
//你可以增加这个数字来填满更多的内存。
const int numMfos = 100000;
// You can increase this number to cause more
// post-finalization work to be done.
//你可以增加这个数字完成终结前的工作。
//const int maxIterations = 10;
static void Main(string[] args)
{
const int c = 100000000;
using (new TestGC("List"))
{
List list = new List();
for (int i = 0; i < c; i++)
{
list.Add(i);
int x=list[i];
}
list = null;
}
using (new TestGC("ArrayList"))
{
ArrayList list1 = new ArrayList();
for (int i = 0; i < c; i++)
{
list1.Add(i);
int x1 =(int) list1[i];
}
list1 = null;
}
using (new TestGC("List"))
{
List list2 = new List();
for (int i = 0; i < c; i++)
{
list2.Add("x");
string x = list2[i];
}
list2 = null;
}
using (new TestGC("ArrayList"))
{
ArrayList list3 = new ArrayList();
for (int i = 0; i < c; i++)
{
list3.Add("x");
string x =(string) list3[i];
}
list3 = null;
}
/*List,执行时间:625ms,垃圾回收次数:6
ArrayList,执行时间:9751ms,垃圾回收次数:393
List,执行时间:1558ms,垃圾回收次数:7
ArrayList,执行时间:1810ms,垃圾回收次数:7*/
Console.ReadKey();
using (new TestGC("我是循环遍历释放引用资源的测试"))
{
int begin_gccount,end_gccount;
PrepareForOperation();
begin_gccount = GC.CollectionCount(0);
MyFinalizeObject mfo = null;
// Create and release a large number of objects
// that require finalization.
/*创建和释放大量对象
这需要最后确定。*/
for (int j = 0; j < numMfos; j++)
{
mfo = new MyFinalizeObject();
}
//Release the last object created in the loop.
mfo = null;
end_gccount = GC.CollectionCount(0);
//Force garbage collection.
//GC.Collect();
// Wait for all finalizers to complete before continuing.
// Without this call to GC.WaitForPendingFinalizers,
// the worker loop below might execute at the same time
// as the finalizers.
// With this call, the worker loop executes only after
// all finalizers have been called.
/*等待所有终结器完成,然后继续。
没有这个电话等待终结器,
下面的工作循环可能同时执行
作为终结者。
通过此调用,工作循环仅在
已调用所有终结器。
含义是等待所有终结器完成*/
//GC.WaitForPendingFinalizers();
//Console.WriteLine("回收次数:"+(end_gccount-begin_gccount));
}
// Worker loop to perform post-finalization code.
//for (int i = 0; i < maxIterations; i++)
//{
// Console.WriteLine("Doing some post-finalize work");
//}
}
internal sealed class TestGC : IDisposable {
private Stopwatch sw=new Stopwatch();
private string text;
private int count;
//构造开始便进行一次重置
public TestGC(string t) {
PrepareForOperation();
text = t;
//同时统计一次垃圾回收次数:
count = GC.CollectionCount(0);
//时间重新开始计时
sw.Start();
}
//using内容结束时,释放资源,统计垃圾回收次数
public void Dispose() {
Console.WriteLine("{0},执行时间:{1}ms,垃圾回收次数:{2}",text,sw.ElapsedMilliseconds,GC.CollectionCount(0)-count);
}
//void IDisposable.Dispose()
//{
// throw new NotImplementedException();
//}
}
///相当于进行垃圾回收重置操作,即先垃圾回收上一次遗留,并确保完成
///并重新开始垃圾回收
private static void PrepareForOperation() {
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
class MyFinalizeObject
{
// Make this number very large to cause the finalizer to
// do more work.
private const int maxIterations = 10000;
~MyFinalizeObject()
{
Console.WriteLine("Finalizing a MyFinalizeObject");
// Do some work.
for (int i = 0; i < maxIterations; i++)
{
// This method performs no operation on i, but prevents
// the JIT compiler from optimizing away the code inside
// the loop.
/*此方法对i不执行任何操作,但阻止
JIT编译器从优化内部代码开始
循环。*/
//阻止内存垃圾回收
GC.KeepAlive(i);
}
}
}
}
花了两三天的时间,将泛型方方面面的知识整理了一遍,主要参考《CLR via C#》,算是比较专业齐全的,
诚恳的说,如果希望能更多收获,提高自己写代码水平,没什么捷径,就是多看多写,多思考总结
比如这里封装,比较代码执行性能的的方法,统计时间和垃圾回收次数的代码。
这里只是个人学习笔记,一来个人能力有限,二来时间仓促,恐有谬误疏忽不足之处,唯有开放共同交流,才能更好的进步,欢迎提各位同仁,留言私信,提出建议交流。
比如第四个知识点:泛型缓存应用,博主就不甚清楚
总之,纸上得来终觉浅,绝知此事要躬行,一起努力吧!