一.什么是泛型

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

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

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

二.泛型的使用和对比

2.1.CommandMethod(普通方法)

         /// <summary>
/// 打印一个Int值
///
/// 因为方法声明的时候,写死了参数类型
/// </summary>
/// <param name="iParameter"></param>
public static void ShowInt(int iParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, iParameter.GetType().Name, iParameter); }
/// <summary>
/// 打印一个string值
/// </summary>
/// <param name="sParameter"></param>
public static void ShowString(string sParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, sParameter.GetType().Name, sParameter);
}
/// <summary>
/// 打印一个DateTime值
/// </summary>
/// <param name="dtParameter"></param>
public static void ShowDateTime(DateTime dtParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, dtParameter.GetType().Name, dtParameter);
}

2.2.ObjectMethod(基类方法)

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

2.3.GenericMethod(泛型方法)

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

2.3调用

                 int iValue = ;
string sValue = "";
DateTime dtValue = DateTime.Now;
object oValue = "";
Console.WriteLine("*********普通方法************");
CommonMethod.ShowInt(iValue);
CommonMethod.ShowString(sValue);
CommonMethod.ShowDateTime(dtValue);
CommonMethod.ShowObject(oValue); Console.WriteLine("*********通过Object************");//.NET Framework1.0 1.1
/*为什么可以传string、int、datetime、class等的原因
1. object类型是一切类型的父类
2. 通过继承,子类拥有父类的一切属性和行为;任何父类出现的地方,都可以用子类来代替
*/
CommonMethod.ShowObject(iValue);
CommonMethod.ShowObject(sValue);
CommonMethod.ShowObject(dtValue); Console.WriteLine("***********通过泛型***************");
GenericMethod.Show<int>(iValue);//需要指定类型参数
GenericMethod.Show<string>(sValue);//必须吻合
GenericMethod.Show<DateTime>(dtValue);//能省略自动推算
GenericMethod.Show<object>(oValue);

2.4.性能对比

     /// <summary>
/// 性能监视类
/// </summary>
public class Monitor
{
public static void Show()
{
int iValue = ;
long commonSecond = ;
long objectSecond = ;
long genericSecond = ; {
//提供一组方法和属性,可用于准确地测量运行时间
Stopwatch stopwatch = new Stopwatch();
//开始或继续测量某个时间间隔的运行时间。
stopwatch.Start();
for (int i = ; i < ; i++)
{
ShowInt(iValue);
}
// 停止测量某个时间间隔的运行时间。
stopwatch.Stop();
//获取当前实例测量得出的总运行时间(以毫秒为单位)。
commonSecond = stopwatch.ElapsedMilliseconds;
}
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = ; i < ; i++)
{
ShowObject(iValue);
}
stopwatch.Stop();
objectSecond = stopwatch.ElapsedMilliseconds;
}
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = ; i < ; i++)
{
Show(iValue);
}
stopwatch.Stop();
genericSecond = stopwatch.ElapsedMilliseconds;
} Console.WriteLine($"普通方法耗时:{commonSecond} Object方法耗时:{objectSecond} Generic方法耗时:{genericSecond}");
} private static void ShowInt(int iParameter)
{ } private static void ShowObject(object oParameter)
{ } private static void Show<T>(T tParameter)
{ } #region Stopwatch详解
/*
获取以每秒计时周期数表示的计时器频率。此字段为只读。
public static readonly long Frequency;
指示计时器是否基于高分辨率性能计数器。此字段为只读。
public static readonly bool IsHighResolution;
初始化 System.Diagnostics.Stopwatch 类的新实例。
public Stopwatch();
获取当前实例测量得出的总运行时间。
  返回结果:
一个只读的 System.TimeSpan,用于表示当前实例测量得出的总运行时间。
public TimeSpan Elapsed { get; }
获取当前实例测量得出的总运行时间(以毫秒为单位)。
  返回结果:
一个只读长整型,表示当前实例测量得出的总毫秒数。
public long ElapsedMilliseconds { get; }
获取当前实例测量得出的总运行时间(用计时器计时周期表示)。
  返回结果:
一个只读长整型,表示当前实例测量得出的计时器计时周期的总数。
public long ElapsedTicks { get; }
获取一个指示 System.Diagnostics.Stopwatch 计时器是否在运行的值。
  返回结果:
如果 System.Diagnostics.Stopwatch 实例当前正在运行,并且在对某个时间间隔的运行时间进行测量,则该值为 true;否则为 false。
public bool IsRunning { get; }
获取计时器机制中的当前最小时间单位数。
  返回结果:
一个长整型,表示基础计时器机制中的计时周期计数器值。
public static long GetTimestamp();
对新的 System.Diagnostics.Stopwatch 实例进行初始化,将运行时间属性设置为零,然后开始测量运行时间。
  返回结果:
刚刚开始测量运行时间的 System.Diagnostics.Stopwatch。
public static Stopwatch StartNew();
停止时间间隔测量,并将运行时间重置为零。
public void Reset();
停止时间间隔测量,将运行时间重置为零,然后开始测量运行时间。
public void Restart();
开始或继续测量某个时间间隔的运行时间。
public void Start();
停止测量某个时间间隔的运行时间。
public void Stop();
*/
#endregion
}
}

2.6对比结果

三.泛型的原理

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

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

     /// <summary>
/// 泛型类
/// 一个类来满足不同的具体类型,来做相同的事
/// </summary>
public class GenericClass<T>
{ } public class GenericClass<T,S> where T:People where S:Hunan
{ } /// <summary>
/// 泛型接口
/// 一个接口来满足不同的具体类型的接口,来做相同的事
/// </summary>
public interface IGenericInterface<T> //where T:People
{ } public class CommonClass:GenericClass<int>//使用泛型必须指定类型
{ } public class GenericClassChild<E>:GenericClass<E>
{ } /// <summary>
/// 泛型委托
/// </summary>
/// <typeparam name="T"></typeparam>
public delegate void GenericDelegate<T>();

五.泛型约束

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

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

     /// <summary>
/// 约束类
/// 泛型:不同的参数类型都能进来;任何类型都来进来,无法准确定位
/// 没有约束,也就没有自由
/// 泛型约束-----基类约束(不能是sealed)
/// 1.可以使用基类的一切属性方法---权利
/// 2.强制保证T一定是People或者People的子类
///
///
/// 为什么不能用int(Int32)?
///“int”不是有效的约束。作为约束使用的类型必须是接口、非密封类或类型参数 而Int32是密封类(sealed)
/// </summary>
public class Constraint
{
public static void Show<T>(T tParameter)
where T : People,ISports,IWork,new()//基类约束
{
Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString()); Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
tParameter.Hi();
tParameter.Pingpang();
tParameter.Work();
} /// <summary>
/// 为什么不用基类
/// </summary>
/// <param name="tParameter"></param>
public static void ShowBase(People tParameter)//因为约束可以叠加 更灵活
{
Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
tParameter.Hi();
} public static T InterfaceGet<T>(T t)
where T : ISports //接口约束
{
t.Pingpang();
return t;
} public static T ClassGet<T>(T t)
where T : class //引用类型约束:保证引用类型
{
T tNew = null;
return t;
} public static T StructGet<T>(T t)
where T : struct //值类型约束
{
T tNew = default(T); //会根据T的不同 赋予默认值
return t;
} public static T NewGet<T>(T t)
where T : new() //无参数构造函数约束
{
T tNew = new T();
return t;
}
}

六.协变和逆变

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

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

  • 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变
  • 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变
    /// <summary>
/// .NET 4.0出现的
/// 只能放在接口或者委托的泛型参数前面
/// out 协变covariant 修饰返回值
/// in 逆变contravariant 修饰传入参数
/// </summary>
public class CCTest
{
public static void Show()
{
{
Bird bird1 = new Bird();
Bird bird2 = new Sparrow();
Sparrow sparrow = new Sparrow();
//Sparrow sparrow=new Bird();//不合理 鸟不一定是麻雀
} {
List<Bird> birdList1 = new List<Bird>();
//两个不同的类型 没有父子关系
//List<Bird> birdList2 = new List<Sparrow>();//应该可以 一堆麻雀是一堆鸟
//在.NET Core中需要引用System.Linq;
List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList();
} {
//协变
IEnumerable<Bird> birdList1 = new List<Bird>();//接口
IEnumerable<Bird> birdList2 = new List<Sparrow>();
Func<Bird> func = new Func<Sparrow>(() => null);//委托
//自定义
ICustomerListOut<Bird> customerList1 = new CustomerListOut<Bird>();
ICustomerListOut<Bird> customerList2 = new CustomerListOut<Bird>();
} {
//逆变
ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Sparrow>();
ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Bird>();
ICustomerListIn<Bird> customerList3 = new CustomerListIn<Bird>();
customerList3.Show(new Bird());
customerList3.Show(new Sparrow());
Action<Sparrow> action = new Action<Bird>((Bird i) => { });
} {
IMyList<Sparrow, Bird> myList1 = new MyList<Sparrow, Bird>();
IMyList<Sparrow, Bird> myList2 = new MyList<Sparrow, Sparrow>();//协变
IMyList<Sparrow, Bird> myList3 = new MyList<Bird, Bird>();//逆变
IMyList<Sparrow, Bird> myList4 = new MyList<Bird, Sparrow>();//逆变+协变
}
}
} /// <summary>
/// 鸟类
/// </summary>
public class Bird
{
public int Id { get; set; }
}
/// <summary>
/// 麻雀类
/// </summary>
public class Sparrow : Bird
{
public string Name { get; set; }
}
/// <summary>
/// in 逆变 只能做参数
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListIn<in T>
{
void Show(T t); //T Get(); 逆变,不能作为返回值,只能把它当成参数
} public class CustomerListIn<T> : ICustomerListIn<T>
{
public void Show(T t)
{ } //public T Get()
//{
// return default(T);
//}
}
/// <summary>
/// out 协变 只能是返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListOut<out T>
{
T Get(); //void Show(T t); 只能放在返回值,不能放在参数
} public class CustomerListOut<T> : ICustomerListOut<T>
{
public T Get()
{
return default(T);
} //public void Show(T t)
//{ //}
} public interface IMyList<in inT,out outT>
{
void Show(inT t); outT Get(); outT Do(inT t); //out 只能是返回值 in只能是参数 } public class MyList<T1, T2> : IMyList<T1, T2>
{
public T2 Do(T1 t)
{
Console.WriteLine(t.GetType().Name);
Console.WriteLine(typeof(T2).Name);
return default(T2);
} public T2 Get()
{
Console.WriteLine(typeof(T2).Name);
return default(T2);
} public void Show(T1 t)
{
Console.WriteLine(t.GetType().Name);
}
}

七.泛型缓存

     public class GenericCacheTest
{
public static void Show()
{
for (int i = ; i < ; i++)
{
Console.WriteLine(GenericCache<int>.GetCache());
Thread.Sleep();
Console.WriteLine(GenericCache<long>.GetCache());
Thread.Sleep();
Console.WriteLine(GenericCache<DateTime>.GetCache());
Thread.Sleep();
Console.WriteLine(GenericCache<string>.GetCache());
Thread.Sleep();
Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
Thread.Sleep();
}
}
}
/// <summary>
/// 字典缓存:静态属性常驻内存
/// </summary>
public class DictionaryCache
{
private static Dictionary<Type, string> _TypeTimeDictionary = null; static DictionaryCache()
{
Console.WriteLine("This is Dictionary 静态构造函数");
_TypeTimeDictionary = new Dictionary<Type, string>();
} public static string GetCache<T>()
{
Type type = typeof(Type);
if (!_TypeTimeDictionary.ContainsKey(type))
{
_TypeTimeDictionary[type] = string.Format("{0}_{1}",typeof(T).FullName,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
}
return _TypeTimeDictionary[type];
}
}
/// <summary>
/// 每个不同的T,都会生成一份不同的副本
/// 适合不同类型,需要缓存一份数据的场景,效率高
/// 不能主动释放
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericCache<T>
{
private static string _TypeTime = "";
static GenericCache()
{
Console.WriteLine("This is GenericCache 静态构造函数");
_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
} public static string GetCache()
{
return _TypeTime;
}
}
/*
* 为什么不用字典缓存?
* 字典缓存是一种哈希分布的,找数据的时候需要把Key进行哈希 找到内存地址 然后才能找到数据 数据如果过多 范围就会很大 造成性能浪费
* 而泛型缓存,每一个都是独立的 不同的实体产生一个不同的副本,副本就存在CPU那,已经存在即时编译器里面去,如果需要找不同的类 直接在内存拿、寻址
* 而字典缓存需要去计算去寻址
*
* 泛型生命周期:永远都不释放
*/

八.泛型总结(图片来源网上)

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

  1. Java - 泛型 ( Generic )

    Java - 泛型 ( Generic )     > 泛型的特点         > 解决元素存储的安全性问题         > 解决获取数据元素时,需要类型强转的问题     ...

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

    Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...

  3. 谈一谈从 Delphi 2009 之后就支援的重要功能 – 泛型 (Generic)

    前言 在C++的语言基础当中,除了物件导向.事件驱动的概念之外,模版设计(Template)也是非常重要的一环.然而,C++的开发人员能够善用模版设计的并不多.模版设计这个好物,一般还有一个名称,就是 ...

  4. JAVA中的泛型(Generic)

    Java泛型(Generic)简介 泛型是jdk1.5版本以后推出来的,表示类型参数化,让java能更具有动态性一些,让类型能变成参数传递. 要我自己感觉的话,泛型本身没啥用,跟反射在一起用,就体现出 ...

  5. Dephi泛型generic的应用

    Dephi泛型generic的应用   泛型在C++, C#中已有广泛应用,Delphi自2009版本也引入泛型,典型的应用如TList,TDictionary.如果你熟悉C#,其用法十分类似. 比如 ...

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

    一.Comparable接口, Collections类 List的常用算法: sort(List); 排序,如果需要对自定义的类进行排序, 那就必须要让其实现Comparable接口, 实现比较两个 ...

  7. Java自学-集合框架 泛型Generic

    ArrayList上使用泛型 步骤 1 : 泛型 Generic 不指定泛型的容器,可以存放任何类型的元素 指定了泛型的容器,只能存放指定类型的元素以及其子类 package property; pu ...

  8. .NET知识梳理——1.泛型Generic

    1. 泛型Generic 1.1        引入泛型:延迟声明 泛型方法声明时,并未写死类型,在调用的时候再指定类型. 延迟声明:推迟一切可以推迟的. 1.2        如何声明和使用泛型 泛 ...

  9. C# 泛型Generic

    泛型(Generic),是将不确定的类型预先定义下来的一种C#高级语法,我们在使用一个类,接口或者方法前,不知道用户将来传什么类型,或者我们写的类,接口或方法相同的代码可以服务不同的类型,就可以定义为 ...

随机推荐

  1. C# Find vs FirstOrDefault

    本文告诉大家,在获得数组第一个元素时,使用哪个方法性能更高. 需要知道,两个方法都是 Linq 的方法,使用之前需要引用 Linq .对于 List 等都是继承可枚举Enumerable这时获取第一个 ...

  2. Laravel 5.3 用户验证源码探究 (一) 路由与注册

    https://blog.csdn.net/realghost/article/details/52558962 简介 Laravel 从 5.2 开始就有了开箱即用的用户验证,5.3 又在 5.2 ...

  3. 测试代码的执行时间魔法方法%time和%timeit

    对于规模更大.运行时间更长的数据分析应用程序,你可能会希望测试一下各个部分或函数调用或语句的执行时间.你可能会希望了解某个复杂计算过程中到底是哪些函数占用的时间最多.幸运的是,在开发和测试代码的过程中 ...

  4. 洛谷P4136 谁能赢呢? 题解 博弈论

    题目链接:https://www.luogu.org/problem/P4136 找规律 首先这道题目我没有什么思路,所以一开始想到的是通过搜索来枚举 \(n\) 比较小的时候的情况. 所以我开搜索枚 ...

  5. jQuery中动态创建、添加元素的方法总结

    <input type="button" value="创建元素" id="btn"> <div id="box ...

  6. HDU 1372

    题意:模拟国际象棋马的走棋方式,和中国象棋一样马走日,8X8的棋盘,问从起点到终点的最短步数,国际象棋中数字代表行row,字母代表列column, 思路:记忆化深搜. #include<cstd ...

  7. vue+element-ui 字体自适应不同屏幕

    项目背景:屏幕自适应问题,当在不同分辨率的屏幕上显示页面时,页面的字体需要根据屏幕大小来自适应,想到使用rem作为字体的单位 vue-cli脚手架下的index.html中写入以下js脚本 <s ...

  8. python的for循环、下标和切片

    for循环的格式   for 临时变量 in 列表或者字符串:     循环满足条件时执行的代码 else:     循环不满足条件时执行的代码   例: name = "abcdef&qu ...

  9. DIRECTORY_SEPARATOR 与 getcwd

    DIRECTORY_SEPARATOR:目录分隔符,linux上就是’/’    windows上是’\’ ,php的内置常量是一个显示系统分隔符的命令,php的内部常量,不需要任何定义与包含即可直接 ...

  10. 高并发下载tomcat下的文件时,发生java.net.SocketException: Connection reset解决方案

    (1)问题产生:使用500个线程并发下载tomcat工程中的一个文件时,服务器出现java.net.SocketException: Connection reset异常, 客户端出现connect ...