(1)泛型的引入
- 泛型--泛:宽泛的、不确定的; 型:类型---不确定的类型
- 泛型方法—做到了性能高—可以一个方法满足不同类型的需求
(2)泛型的声明
- 泛型方法: 在方法名称后面多了一个尖括号,尖括号中有占位符
- 延迟声明:声明的时候,只是给一个占位符T,T是什么类型? 不知道什么类型--调用的时候,指定类型
- 占位符:T --类型参数---类型变量
- 类型参数当作方法的参数的时候,明确参数类型
(3)泛型的特点和原理
在高级语言中,定义泛型T,在计算机执行的时候,一定要是一个具体类型
在底层如何支持?
在底层生成的结果是:List1[T] Dictinary
2[Tkey,TValue],在底层生成了带有1、
2、`3.....
编译器必须要能够支持泛型
CLR运行时环境也必须要支持泛型
泛型是框架的升级支持的,泛型不是语法糖
语法糖是编译器提供的便捷功能--.NetFramework2.0时代支持的
(4)泛型的应用
-
泛型方法---可以一个方法满足不同类型的需求
泛型方法的定义如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyGeneric
{
public class CommonMethod
{
/// <summary>
/// 泛型方法
/// </summary>
/// <typeparam name="T">类型变量 类型参数</typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name},parameterType={tParameter.GetType().Name},value={tParameter}");
}
}
}
泛型方法的调用
using MyGeneric;
try
{
Console.WriteLine("今天知识点泛型!");
int iValue = 123;
string sValeu = "我是泛型";
DateTime dtValue = DateTime.Now;
object oValue = "234";
//调用泛型方法
CommonMethod.Show<int>(iValue); //如果可以通过参数推导出类型-- - 尖括号可以省略
CommonMethod.Show<string>(sValeu);// 省略类型--语法糖
CommonMethod.Show<DateTime>(dtValue);
CommonMethod.Show<object>(oValue);
}
catch (Exception)
{
throw;
}
- 泛型接口---可以一个接口满足不同类型的需求---尖括号+占位符 泛型接口的定义
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyGeneric
{
public interface GenericInterface<T>
{
public T Show();
}
}
调用实例:
using MyGeneric.Extension;
using System;
using System.Collections.Generic;
namespace MyGeneric
{
class Program
{
static void Main(string[] args)
{
try
{
{
GenericInterfac<string> sgenericInterfac = null;
GenericInterfac<DateTime> dtgenericInterfac = null;
}
}catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
4. 泛型类------可以一个类型满足不同类型的需求---尖括号+占位符
```js
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyGeneric
{
public abstract class GenericAbstractClass<T>
{
public void Show(T t){}
}
public class ChildClass1<S> : GenericInterfac<int>
{
}
}
调用实例:
using MyGeneric.Extension;
using System;
using System.Collections.Generic;
namespace MyGeneric
{
class Program
{
static void Main(string[] args)
{
try
{
{
GenericAbstractClass<string> sGenericClass = null;
GenericAbstractClass<DateTime> dtGenericClass = null;
GenericAbstractClass<int> iGenericClass = null;
//sGenericClass dtGenericClass iGenericClass 三者是什么关系?--一毛钱关系都没有
}
}catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
5. 泛型委托----可以一个委托满足不同类型的需求
```js
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyGeneric
{
public delegate T Genericdelegate<T>();
}
调用实例:
using MyGeneric.Extension;
using System;
using System.Collections.Generic;
namespace MyGeneric
{
class Program
{
static void Main(string[] args)
{
try
{
{
Genericdelegate<string> sGenericdelegate = null;
Genericdelegate<DateTime> dtGenericdelegate = null;
}
}catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
(5)泛型约束和泛型缓存
泛型约束
约束 | 描述 |
---|---|
where T :struct | 类型参数必须是不可为null的值类型。由于所有值类型都具有可访问的无参数构造函数,因此 struct 约束表示new() 约束,并且不能与new() 约束结合使用。struct 约束也不能与unmanaged 约束结合使用。 |
where T :class | 类型参数必须是引用类型。此约束还应用于任何类、接口、委托或者数组类型。可为null的上下文中,T必须是不可为null的引用类型 |
where T :class ? | 类型参数必须是可为 null 或不可为 null 的引用类型。 此约束还应用于任何类、接口、委托或数组类型。 |
where T :notnull | 类型参数必须是不可为 null 的类型。 参数可以是不可为 null 的引用类型,也可以是不可为 null 的值类型。 |
where T :default | 重写方法或提供显式接口实现时,如果需要指定不受约束的类型参数,此约束可解决歧义。 default 约束表示基方法,但不包含 class 或 struct 约束。 有关详细信息,请参阅default 约束规范建议。 |
where T :new() | 类型参数必须具有公共无参数构造函数。 与其他约束一起使用时,new() 约束必须最后指定。 new() 约束不能与 struct 和 unmanaged 约束结合使用。 |
where T :基类名称 | 类型参数必须是指定的基类或派生自指定的基类。 在可为 null 的上下文中,T 必须是从指定基类派生的不可为 null 的引用类型。 |
where T : <基类名>? | 类型参数必须是指定的基类或派生自指定的基类。 在可为 null 的上下文中,T 可以是从指定基类派生的可为 null 或不可为 null 的类型。 |
where T : <接口名称> | 类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。 在的可为 null 的上下文中,T 必须是实现指定接口的不可为 null 的类型。 |
where T : <接口名称>? | 类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。 在可为 null 的上下文中,T 可以是可为 null 的引用类型、不可为 null 的引用类型或值类型。 T 不能是可为 null 的值类型。 |
where T : U | 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。 在可为 null 的上下文中,如果 U 是不可为 null 的引用类型,T 必须是不可为 null 的引用类型。 如果 U 是可为 null 的引用类型,则 T 可以是可为 null 的引用类型,也可以是不可为 null 的引用类型。 |
where T : unmanaged | 类型参数必须是不可为 null 的非托管类型。 unmanaged 约束表示 struct 约束,且不能与 struct 约束或 new() 约束结合使用。 |
- 基类约束
把类型参数当作People
调用的时候只能传递people或者people的子类型
泛型约束,要么不让你进来,如果让你进来就一定是没问题
public static void ShowBase<T>(T tParameter) where T : People
{
}
- 接口约束
把这个T当作ISports
只能传递ISports这个接口或者是实现这个接口的类
通过接口约束可以增加功能
public static void ShowInterface<T>(T tParameter) where T :ISport
- 引用类型约束
只能传递引用类型进来
public static void ShowClass<T>(T tParameter) where T : class
{
//T t = new T(); //因为T 可能没有无参数构造构造函数
}
- 值类型约束
只能传递值类型进来
由于所有值类型都具有可访问的无参数构造函数,因此struct约束表示new()约束,并且不能与new()约束结合使用
public static void ShowStruct<T>(T tParameter) where T : struct
{
T t = new T();
}
- 无参构造函数约束
必须有一个无参数构造函数才能当做参数传入
public static void ShowNew<T>(T tParameter) where T : new()
{
T t = new T();
}
- 枚举类型约束
必须是一个枚举类型才能传入
public static void ShowEnum<T>(T tParameter) where T : Enum
{
}
(6)协变和逆变
是一种高级约束
是为了规避下面两种情况
1.把子类做参数,却把父类当参数传入;
2.把子类做返回值,却返回的时候,返回了一个父类
-
-协变 out:修饰类型参数;就可以让右边用子类,能让左边用父类
-
-逆变in:修饰类型参数;就可以让右边用父类;左边用子类
-
协变和逆变只针对泛型接口和泛型委托