1.什么是泛型
应该都听过刘谦的魔术《魔壶》吧,想要什么味道的饮料都可以倒出来。在C#中,可以将泛型比作刘谦手上的壶,我们需要的类型比作壶倒出来的饮料。在碰到一些功能相似,类型不同的方法时,我们就可能会用到泛型。当然object也能达到泛型的效果,但是泛型不是object,他们有很大的区别。
2.泛型和方法
在之前上网课的时候有老师举出了一些代码示例,在这就先借鉴一下:
首先创建一个类
using System;
namespace Generic
{
public class MyGenericClass
{
public static void showInt(int iParameter)
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyGenericClass).Name, iParameter.GetType().Name, iParameter);
}
public static void showString(string sParameter)
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyGenericClass).Name, sParameter.GetType().Name, sParameter);
}
public static void showDateTime(DateTime dtParameter)
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyGenericClass).Name, dtParameter.GetType().Name, dtParameter);
}
}
}
调用一下:
using System;
namespace Generic
{
internal class Program
{
private static void Main(string[] args)
{
MyGenericClass.showInt(1);
MyGenericClass.showString("2");
MyGenericClass.showDateTime(DateTime.Now);
Console.ReadKey();
}
}
}
输出结果:
This is MyGenericClass, type = Int32, param=1
This is MyGenericClass, type = String, param=2
This is MyGenericClass, type = DateTime, param=2019/7/1 15:43:54
因为object是所有类型的父类,根据继承的原则,父类可以充当子类来使用,所以MyGenericClass也可以改为:
public static void showobject(object oParameter)
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyClassTemplate).Name, oParameter.GetType().Name, oParameter);
}
上文有说道object也可以做到跟泛型类似的功能,通过简单的修改就可以将object改写为泛型方法:
我们将MyGenericClass中的object方法使用泛型来改写:
public static void showGeneric<T>(T tParameter)
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyClassTemplate).Name, tParameter.GetType().Name, tParameter);
}
调用一下:
using System;
namespace Generic
{
internal class Program
{
private static void Main(string[] args)
{
//MygenericClasee.showGeneric<int>(1);
MyGenericClass.showGeneric(1);
MyGenericClass.showGeneric("2");
MyGenericClass.showGeneric(DateTime.Now);
Console.ReadKey();
}
}
}
输出结果
This is MyGenericClass, type = Int32, param=1
This is MyGenericClass, type = String, param=2
This is MyGenericClass, type = DateTime, param=2019/7/1 15:43:54
那么object到底和泛型有什么区别呢,我们来看下:
新建一个类用于记录程序运行时间:
using System;
using System.Diagnostics;
namespace Generic
{
public class Monitor
{
public static void Show()
{
Console.WriteLine("======================");
int iValue = 123456;
long intSecond = 0;
long objectSecond = 0;
long genericSecond = 0;
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 500000000; i++)
{
ShowInt(iValue);
}
watch.Stop();
intSecond = watch.ElapsedMilliseconds;
watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 500000000; i++)
{
ShowObject(iValue);
}
watch.Stop();
objectSecond = watch.ElapsedMilliseconds;
watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 500000000; i++)
{
ShowGeneric(iValue);
}
watch.Stop();
genericSecond = watch.ElapsedMilliseconds;
Console.WriteLine("commonSecond={0},objectSecond={1},genericSecond={2}"
, intSecond, objectSecond, genericSecond);
}
//这里在方法中不要写任何代码,执行时间过久
private static void ShowInt(int iParameter){}
private static void ShowObject(object sParameter){}
private static void ShowGeneric<T>(T tParameter){}
}
}
各个方法运行5亿次后的输出结果:
commonSecond=1591,objectSecond=2593,genericSecond=1694
我们可以看出执行速度(也可以说是性能)子类型≈泛型<object,这是因为在使用object的时候会涉及到一个装箱拆箱的操作,在做拆箱装箱操作是会损耗程序的性能。还有一点泛型和子类型执行速度那个快,需要看代码的复杂程度。
为什么泛型的性能高于object?
泛型是延时声明的,在定义的时候不需要指定具体的参数类型,只有在调用的时候才会去制定具体的参数类型,类似与List,和Dictionary
Console.WriteLine(typeof(List<>));
Console.WriteLine(typeof(Dictionary<,>));
System.Collections.Generic.List`1[T]
System.Collections.Generic.Dictionary`2[TKey,TValue]
从输出结果来看List和Dictionary后方~跟的是数据类型,只是一个占位符。在经过JIT编译的时候才会给与对应的数据类型。
相似的还有泛型类,泛型接口,泛型委托等。
3.泛型约束
新建类MyGeneric.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Generic
{
//泛型接口
public interface IInterface<T>
{
T genericInterface(T t);
}
//泛型接口继承,参数类型可以为泛型
public interface ISubInterface<T> : IInterface<T>
{
}
//普通接口
public interface INormalInterface
{
void Hi();
}
public class MyGeneric : ISubInterface<string>
{
public string genericInterface(string t)
{
return t;
}
}
public class cooker
{
public int id { get; set; }
public string name { get; set; }
public void cook()
{
Console.WriteLine("I can cook");
}
}
public class spoken : cooker, INormalInterface
{
public void Hi()
{
Console.WriteLine("Hi");
}
}
public class emily : spoken
{
}
}
Main函数中赋值,调用泛型方法
emily emily = new emily()
{
id = 345,
name = "emily"
};
MyClassTemplate.ShowGeneric(emily);
修改泛型方法
public static void ShowGeneric<T>(T tParameter) where T : emily
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyClassTemplate).Name, tParameter.GetType().Name, tParameter);
var id = tParameter.id;
var name = tParameter.name;
tParameter.Hi();
}
修改后的泛型方法可以打印出指定传入参数的属性值。泛型约束中emily有id和name属性,但是如果在使用object方法的时候,object并没有id和name属性,所以会报错。
3.5种泛型约束
where T : struct | 类型必须是一种值类型(struct) |
here T :class | 类型必须是一种引用类型(class) |
where T : new() | 类型必须有一个无参数的构造器 |
where T : class_name | 类型可以是class_name或者是它的一个子类 |
where T : interface_name | 类型必须实现指定的接口 |
a.基类约束
/// <summary>
/// 基类约束:T必输是Class类型或者Class的子类
/// </summary>
public class MyClass<T>(T tParameter) where T:Class{}
///example
public static void ShowGeneric<T>(T tParameter) where T : emily
{
Console.WriteLine("This is {0}, type = {1}, param={2}",
typeof(MyClassTemplate).Name, tParameter.GetType().Name, tParameter);
var id = tParameter.id;
var name = tParameter.name;
tParameter.Hi();
}
MyClassTemplate.ShowGeneric(emily);
b.接口约束
/// <summary>
/// 接口约束
/// </summary>
public static T Get<T>(T t) where T : IGemericInterface
{
t.XXX();
return t;
}
c.引用类型约束
/// <summary>
/// 引用类型约束
/// </summary>
public static T Get<T>(T t) where T : class
{
return t;
}
d.值类型约束 struct
/// <summary>
/// 值类型类型约束
/// </summary>
public static T Get<T>(T t) where T : struct
{
return t;
}
e.无参数构造函数约束 new()
/// <summary>
/// new()约束
/// </summary>
public static T Get<T>(T t) where T : new()
{
return t;
}