一、Generic(泛型)
1、泛型的定义:通用的类型就是泛型
//在一个方法,传入的参数不确定的时候,我们可能要重写多次这个方法
public void Show(string t)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
public void Show(int t)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
//这种情况也可以将参数定义为object类型来解决这个问题
public void Show(object t)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
但是object有两个缺陷:
1、类型安全问题
2、性能非常差
通过下面的类子来证明这个问题
using System.Diagnostics;
namespace MyGeneric
{
public class Monitor
{
public static void Show()
{
int iValue = 12345;
long commonSecond = 0;
long objectSecond = 0;
long genericSecond = 0;
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100_000_000; i++)
{
ShowInt(iValue);
}
sw.Stop();
commonSecond = sw.ElapsedMilliseconds;
}
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100_000_000; i++)
{
ShowObject(iValue);
}
sw.Stop();
objectSecond = sw.ElapsedMilliseconds;
}
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100_000_000; i++)
{
ShowGeneric(iValue);
}
sw.Stop();
genericSecond = sw.ElapsedMilliseconds;
}
Console.WriteLine($"commonSecond={commonSecond},objectSecond={objectSecond},genericSecond={genericSecond}");
}
public static void ShowInt(int iValue) { }
public static void ShowObject(object oValue){}
public static void ShowGeneric<T>(T t) { }
}
}
//所以我们需要泛型来解决这个问题,泛型的性能和固定类型的性能没有什么差距
public void Show<T>(T t)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
2、泛型的声明---设计思想
方法在声明的时候,带有尖括号<>,尖括号里有一个占位符T;
T是什么?什么类型? 称之为类型参数
延迟声明:声明时不确定类型,调用时确定类型
3、泛型的特点和原理---在底层如何支持
1、泛型在编译后,变成一个带有 `符号[位数]
2、泛型类在继承时,子类继承的父类
4、泛型的多种应用
1、泛型方法----方法名<T>
2、泛型类---类名<T>,特点:为每一个不同类型生成一个唯一的副本
3、泛型接口--接口名<T>
4、泛型委托--Action<int>,Func<int>系统自带的两个泛型委托
5、泛型约束--
5、泛型缓存
using System;
using System.Collections.Generic;
using System.Threading;
namespace MyGeneric.Extension
{
//能理解80%以上 刷个1
//能理解60%以上 刷个2
/// <summary>
/// 泛型缓存~
/// </summary>
public class GenericCacheTest
{
public static void Show()
{
//普通缓存
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(DictionaryCache.GetCache<int>()); //GenericCacheInt
Thread.Sleep(10);
Console.WriteLine(DictionaryCache.GetCache<long>());// GenericCachelong
Thread.Sleep(10);
Console.WriteLine(DictionaryCache.GetCache<DateTime>());
Thread.Sleep(10);
Console.WriteLine(DictionaryCache.GetCache<string>());
Thread.Sleep(10);
Console.WriteLine(DictionaryCache.GetCache<GenericCacheTest>());
Thread.Sleep(10);
}
}
//泛型缓存--可以根据不同的类型生成一个新的类的副本;生合二老无数的副本;
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(GenericCache<int>.GetCache()); //GenericCacheInt
Thread.Sleep(10);
Console.WriteLine(GenericCache<long>.GetCache());// GenericCachelong
Thread.Sleep(10);
Console.WriteLine(GenericCache<DateTime>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<string>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
Thread.Sleep(10);
}
}
}
}
/// <summary>
/// 字典缓存:静态属性常驻内存
/// </summary>
public class DictionaryCache
{
/// <summary>
/// 静态字典---常驻内存~ 只要程序进程不停止,就会一直保存数据在内存;
/// </summary>
private static Dictionary<Type, string> _TypeTimeDictionary = null;
//同一个类的静态构造函数在整个进程中,执行且只执行一次;
static DictionaryCache()
{
Console.WriteLine("This is DictionaryCache 静态构造函数");
_TypeTimeDictionary = new Dictionary<Type, string>();
}
public static string GetCache<T>()
{
Type type = typeof(T); //获取类的type 类型
if (!_TypeTimeDictionary.ContainsKey(type))
{
//写死的,生成数据; 使用当前这个type 作为key 保存到字典中去
_TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
}
return _TypeTimeDictionary[type];
}
}
/// <summary>
///泛型缓存:
///本质就是泛型类---泛型缓存的应用--手写ORM----有应用~~
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericCache<T>
{
/// <summary>
///同一个类的静态构造函数在整个进程中,执行且只执行一次;
/// </summary>
static GenericCache()
{
Console.WriteLine("This is GenericCache 静态构造函数");
//_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
_TypeTime = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
}
/// <summary>
/// 只缓存一个字符串
/// </summary>
private static string _TypeTime = "";
public static string GetCache()
{
return _TypeTime;
}
}
}
6、泛型约束
1、C#中引用类型-->保存在进程堆,值类型-->保存在线程栈,从值类型转换为引用类型叫装箱,从引用类型转换为值类型叫拆箱
2、类型安全:允许传入,但是在方法内部又写了类型强转等,在执行的时候会报错。
public static void ShowObject(object oParameter)
{
//Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={oParameter.GetType().Name},type={oParameter}");
//以上业务其实太简单了点;
//如果想要用People呢?
//C#是强类型语言,编译时决定类型;
//编译器识别的 oParameter 当成 object来识别的;而object 是没有Id和Name
//访问不了
//Console.WriteLine($"People.Id={oParameter.Id}");
//Console.WriteLine($"People.Name={oParameter.Name}"); //访问不了
//类型转换
People people = (People)oParameter; //类型强转~
Console.WriteLine($"People.Id={people.Id}");
Console.WriteLine($"People.Name={people.Name}"); //访问不了
}
////类型不安全:
////object 类型作为参数,所有类型都可以传入;
////在方法的内部业务处理,可能会因为一些不适合的类型传入,导致一些问题;
////特点: 允许你传入
//// 可能会报错~~
GenericConstraint.ShowObject(people);
GenericConstraint.ShowObject(dtValue);
////最好的情况: 如果方法内部会出错,就应该不让你传入; 如果能传入,就要保证一定没问题;
3、泛型约束:
//特点:
//有了约束---传入参数有了局部限制;
//可以享有一些额外的权利~~
//有约束才有自由~~ 开车~~ 红绿灯;
/// <summary>
/// 基类约束:
/// 泛型的类型参数,必须是People或者是People的子类
/// 否则无法传入;
/// 就可以把这个类型参数当做People来使用;
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tParameter"></param>
public static void ShowGeneric<T>(T tParameter) where T : People
{
//Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={tParameter.GetType().Name},type={tParameter}");
Console.WriteLine($"People.Id={tParameter.Id}");
Console.WriteLine($"People.Name={tParameter.Name}"); //访问不了
}
///接口约束
///类型参数:必须实现这个接口,才能够传入进来;
///就可以把参数当成这个接口使用
public static void ShowGeneric<T>(T tParameter) where T : IWork
{
tParameter.Work();
}
///无参数构造函数约束
///传入的参数,必须包含一个无参数构造函数
///就可以直接new T 执行无参数构造函数~
public static void ShowGeneric<T>(T tParameter) where T : new()
{
T tt = new T();
}
///值类型约束
///必须要传入结构类型,否则无法传入的
public static void ShowGeneric<T>(T tParameter) where T : struct
{
}
///引用类型约束
///必须要传入引用类型,否则无法传入的
public static void ShowGeneric<T>(T tParameter) where T : class
{
}
///枚举约束
///要求传入的参数必须是一个枚举
public static void ShowGeneric<T>(T tParameter) where T : Enum
{
}
public static void ShowGenericNew<T,S,Y,Richard>(T tParameter) where T : struct
{
}
public static void ShowGenericNew2<T, S>(T tParameter) where T : class where S : struct
{
}
7、协变、逆变
1、协变逆变:只针对泛型接口和泛型委托
//任何子类都可以用父类来声明
Animal animal = new Cat();//猫是一个动物
//但是在集合中这样声明会报错
List<Animal> animalList = new List<Cat>();//一堆猫是一堆动物,逻辑上是对的,但是声明的时候会报错
//因为泛型类在传入一个类的类型后,就生成了一个独立的副本,两边生成的副本都是相互独立的,所以左右两边的副本不存在继承关系
//以上的不和谐,引入的协变逆变
//协变,IEnumerable的泛型类型参数用out修饰,类型参数只能作为返回值,不能作为传入参数
//效果:可以让左边使用父类,右边使用子类
IEnumerable<Animal> animals = new List<Cat>();
//逆变, in修饰T,T只能做参数,不能做返回值
//效果:左边使用子类,右边使用父类
标签:Console,进阶,C#,static,WriteLine,泛型,public,Name
From: https://www.cnblogs.com/SevenDouble/p/18647445