泛型方法就是一个方法能满足不同类型的方法。
泛型接口就是一个能满足不同类型需求的接口。
泛型委托就是一个能满足不同类型需求的委托。
1.object是所有类的基类,所以也可以实现和泛型一样的功能(比如接收参数的时候可以接收任何类型的参数),但是使用object存在2个缺陷
(1)类型安全问题
(2)性能问题(拆箱和装箱,其中涉及到了值类型和引用类性)
object是引用类型,比如方法接收一个int参数,首先要将int转为object,此时需要在堆中开辟空间存储值,进入方法内部实际使用的时候需要拆箱,就是将堆中存储的数据取出来放到栈中,这个过程会损失性能。
(3)泛型为什么能识别任何类型?
如下,是c#语言编译的过程, CLR是托管程序运行的环境,就像Windows是普通的PE程序的运行环境一样,JIT即时编编译,是.NET运行可执行程序的基本方式,比在真正运行的时候IL才会被JIT编译成机器指令。
注意:泛型是框架升级之后所带的功能,不是语法糖
(4)调用方式
明确限制传入类型:此时已经限制了只能传int类型,传别的类型会报错。
public void Template() { int s = 0; Test<int>(s); } /// <summary> /// 声明一个泛型方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="test"></param> public void Test<T>(T test) { }
第二种是由编译器编译出来传入的类型,不明显指定:
public void Template() { int s = 0; Test(s); }
(5)性能对比:泛型>普通方法>object(前提是在大量数据数据情况下,数据量不是很大的时候其实差距可以忽略不计)
2.泛型缓存 (泛型缓存性能要大于普通的缓存)
(1)在这之前先做一个使用静态属性常驻内存的缓存形式,这也是比较常用的。
public class Program { static void Main(string[] args) { TestCashe.GetCashe("1"); } } public class TestCashe { public static Dictionary<string, string> _dic = null; static TestCashe() { Console.WriteLine("构造静态函数"); _dic = new Dictionary<string, string>(); } //存储/获取 缓存数据 public static string GetCashe(string key) { if (!_dic.ContainsKey(key)) { _dic[key] = DateTime.Now.ToString(); } Console.WriteLine($"输出{_dic[key]}"); return _dic[key]; } }
结果:
构造静态函数
输出2021/8/14 20:01:24
(2)泛型类型会针对传入的不同类型生成对应的副本,比如一个正常的类中的一个静态构造函数,在new这个类的时候,只会执行一遍这个静态构造函数,但是在泛型类中,如果传入的这个类型是第一次进入,就会执行静态函数,如果第二次再传已经传入过的类型之后静态构造函数就不会再执行了,因为这个类型的副本已经存在了。
namespace MyDelegateExtend { public class Program { static void Main(string[] args) { TestCashe<int>.GetCashe(); } } public class TestCashe<T> { public static string _str = null; static TestCashe() { _str = $"类型:{typeof(T).FullName},时间: DateTime.Now.ToString()"; Console.WriteLine($"构造静态函数:{DateTime.Now.ToString()},类型:{typeof(T)}"); } //存储/获取 缓存数据 public static string GetCashe() { Console.WriteLine($"输出:{_str}"); return _str; } } }
结果:
构造静态函数:2021/8/14 19:55:27,类型:System.Int32
输出:类型:System.Int32,时间: DateTime.Now.ToString()
3.泛型类继承
public class Program { static void Main(string[] args) { ChildGenericTest01<string> Test01 = new ChildGenericTest01<string>(); //这是子类和父类同一类型的时候,如果父类指定了具体的类型,比如int,在这里肯定就会报错 string test = Test01.GetTest("哈哈"); Console.WriteLine($"结果:{test}"); } } /// <summary> /// 泛型类 /// </summary> /// <typeparam name="T"></typeparam> public class GenericTest<T> { public T GetTest(T str) { return str;//或者默认类型值return default(T) } } /// <summary> /// 第一种继承方式:子类不是泛型类的时候,需要给父类一个具体的类型,比如int。 /// </summary> public class ChildGenericTest : GenericTest<int> { } /// <summary> /// 第二种方式:子类同样是泛型类的话,可以让父类依赖子类的类型, /// 也可以和第一种一个指定具体的类型。 /// </summary> /// <typeparam name="S"></typeparam> public class ChildGenericTest01<S> : GenericTest<S> { public ChildGenericTest01() { Console.WriteLine($"ChildGenericTest01 构造函数 S:{typeof(S)}"); } }
结果:
ChildGenericTest01 构造函数 S:System.String
结果:哈哈
4:泛型约束
(1)泛型还存在类型安全的问题:如下代码,因为GetAllInfo方法参数是object,它是所有类型的基类,所以都能接收,但就是什么都能接收,很可能会造成方法内部代码出现问题。
public class Program { static void Main(string[] args) { Chinese chinese = new Chinese() { Id = "1", Name = "chinese" }; Shandong Shandong = new Shandong() { Id = "1", Name = "Shandong" }; Car Car = new Car() { Id = "1", Name = "Car" }; // GetAllInfo(chinese); GetAllInfo(Shandong); //在执行此方法时候报错,因为Car没有继承People,所以内部方法转化的时候报错,这就是类型安全问题 GetAllInfo(Car); } /// <summary> /// object是所有的类的基类 /// </summary> /// <param name="parameter"></param> public static void GetAllInfo(object parameter) { Peopple peopple = (Peopple)parameter; Console.WriteLine($"People.Id={peopple.Id}"); } } public class Peopple { public string Id { get; set; } public string Name { get; set; } } public class Chinese : Peopple { } public class Shandong : Peopple { } /// <summary> /// 汽车类,不继承People /// </summary> public class Car { public string Id { get; set; } public string Name { get; set; } }
所以我们可以使用泛型进行约束,比如只允许People以及其子类才能调用方法,也可以称之为基类约束
/// <summary> /// 基类约束 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="parameter"></param> public static void GetAllInfoBase<T>(T parameter) where T:Peopple { Console.WriteLine($"People.Id={parameter.Id}"); }
类似的约束还有接口约束:
namespace MyDelegateExtend { public class Program { static void Main(string[] args) { Chinese chinese = new Chinese() { Id = "1", Name = "chinese" }; Shandong Shandong = new Shandong() { Id = "1", Name = "Shandong" }; Car Car = new Car() { Id = "1", Name = "Car" }; //代码正常 GetAllInfoInterface(chinese); //下面2行代码报错,因为没有实现ISports接口 GetAllInfoInterface(Shandong); GetAllInfoInterface(Car); } /// <summary> /// 接口约束:只有ISports接口以及实现类才能使用这个方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="parameter"></param> public static void GetAllInfoInterface<T>(T parameter) where T : ISports { //此时下面的代码会报错,因为ISports接口中没有Id属性, //Console.WriteLine($"People.Id={parameter.Id}"); //此时下面的代码正常,因为存在Pingabng方法 Console.WriteLine($"Pingabng={parameter.Pingabng()}"); } } public class Peopple { public string Id { get; set; } public string Name { get; set; } } public interface ISports { string Pingabng(); } public class Chinese : Peopple, ISports { public string Pingabng() { return ""; } } public class Shandong : Peopple { } /// <summary> /// 汽车类,不继承People /// </summary> public class Car { public string Id { get; set; } public string Name { get; set; } } }
引用类性约束:
/// <summary> /// 引用类性约束 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="parameter"></param> public static void GetAllInfoClass<T>(T parameter) where T : class { Console.WriteLine($"parameter={typeof(T)}"); }
值类型约束:
/// <summary> /// 值类性约束 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="parameter"></param> public static void GetAllInfoStruct<T>(T parameter) where T : struct { Console.WriteLine($"parameter={typeof(T).Name}"); }
无参构造函数约束:
/// <summary> /// 无参数构造函数约束:必须要有一个无参的构造函数 /// 一定要注意,类会默认存在无参构造函数,如果你手动创建了带参函数,那么无参构造函数 /// 就会消失,如果有需要的话可以手动写出来 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="parameter"></param> public static void GetNew<T>(T parameter) where T : new() { Console.WriteLine($"parameter={typeof(T).Name}"); }
总结:泛型约束在实际开发项目中不会出现类性安全问题,可以让开发者合理调配灵活运用。需要根据业务需求选择是否使用泛型约束。