什么是泛型?
泛型(generic)是C# 2.0推出的新语法,并不是语法糖,它是专门为处理多段代码在不同的数据类型上执行相同的指令的情况而设计的。
泛型是一个复合类型,把多个类型混合一起作用,比如:方法和泛型混到一起,叫泛型方法,类和泛型混在一起叫泛型类,接口和泛型混到一起,叫泛型接口,等等。
泛型字面意思广泛的类型,其实泛型是C#语言的一种特性。泛型可以理解为一种参数化类型,其实泛型是C#语言的一种特性。泛型可以理解为一种参数化类型,即把类型定义成参数形式,类似与方法在的变量参数。
泛型只存在于编译时期,在运行时期会被擦除。
如何定义:
泛型定义语法格式:<T>或<T,K,......> 其中T,K指未知类型。
泛型定义使用 <类型> 语法,如:public void SayHello<string>(string content);
泛型定义时,是延迟声明的:即定义的时候没有指定具体的参数类型,把参数类型的声明推迟到了调用的时候才指定参数类型。
为什么使用泛型?
即泛型让不同的数据类型支持相同的业务逻辑。
static void Main(string[] args)
{
MyGenaric genaric=new MyGenaric();
//在没有使用泛型之前我们要针对于 int、double、string、Student类型各写一个方法去调用
genaric.ShowIntFunc(1);
genaric.ShowStringFunc("a");
genaric.ShowDoubleFunc(1.5);
genaric.ShowStudentInfo(new Student());
Console.WriteLine("___________________________________________");
//在使用泛型之后我们只需要调用一个泛型方法去动态的识别我们参数的类型
genaric.ShowGenaricFun(1);
genaric.ShowGenaricFun("a");
genaric.ShowGenaricFun(1.5);
genaric.ShowGenaricFun(new Student());
Console.ReadKey();
}
}
public class MyGenaric()
{
public void ShowIntFunc(int a)
{
Console.WriteLine($"参数{a}的类型为int");
}
public void ShowStringFunc(string a)
{
Console.WriteLine($"参数{a}的类型为string");
}
public void ShowDoubleFunc(double a)
{
Console.WriteLine($"参数{a}的类型为double");
}
public void ShowStudentInfo(Student student)
{
Console.WriteLine($"参数{student}的类型为Student");
}
public void ShowGenaricFun<T>(T t)
{
Console.WriteLine($"参数{t}的类型是{typeof(T).Name}");
}
}
public class Student
{
public int SId { get; set; }
public string SName { get; set; }
public bool Sex { get; set; }
}
泛型的优势:
速度块且支持不同类型
a:最大限度的重用代码(支持多种数据类型)
b:保护类型的安全以及提高性能(和object相比,object会使用到装箱操作)
好处:
-
类型安全:泛型确保了数据结构中存储的数据类型是正确的,减少了运行时错误。
-
性能提升:由于泛型在编译时就已经确定了类型,因此避免了运行时的类型转换,提高了性能。
-
代码重用:泛型允许开发者编写一次代码,然后用于多种数据类型,从而减少了代码重复。
-
编译时检查:泛型在编译时进行类型检查,这有助于及早发现错误,避免在运行时出现问题。
-
减少装箱和拆箱操作:对于值类型,使用泛型可以避免不必要的装箱和拆箱操作,这在性能敏感的应用中非常重要。
-
提高代码的可读性:泛型使得代码更加清晰,其他开发者可以更容易地理解代码的意图。
-
支持协变和逆变:C# 支持泛型的协变和逆变,这使得泛型接口和委托更加灵活。
-
支持约束:泛型可以应用类型约束,确保泛型类型参数满足特定的要求,如实现特定的接口或继承自特定的类。
-
易于维护:由于泛型减少了类型相关的错误,维护起来更加容易。
-
支持泛型方法和属性:除了泛型类和结构体,C# 还支持定义泛型方法和属性,进一步增强了泛型的灵活性。
c:语法优美
泛型应用范围?
泛型方法,泛型类,泛型接口,泛型委托,泛型结构
泛型方法:
Show<T>是方法名称 <T>T是未知类型,不区分大小写,但建议使用大写,且尽量使用如下的字母: T,K,V,M,N
泛型方法:调用时泛型参数类型可以省略。
泛型方法:方法名称后面加上尖括号,里面是类型参数
类型参数实际上就是一个类型T声明,方法就可以用这个类型T了
定义时注意几点:
1。参数的未知类型在哪定义?(方法名称后面<>中)
2。参数的未知类型在哪里可以使用?(方法局部作用域)
3。参数的未知类型能定义几个?理论上是无限
泛型类:
namespace 练习
{
internal class Program
{
//泛型类
static void Main(string[] args)
{
MyClass<int> myClass = new MyClass<int>();
myClass.UP(12);
}
}
public class MyClass<T>
{
public void UP(T value)
{
Console.WriteLine(value);
}
}
泛型接口:
接口使用interface关键字来定义
泛型接口和泛型类定义方式一样,在接口名称后使用<T>未知类型
泛型接口声明:
interface [接口名]<T>
{
[接口体]
}
//泛型接口
public interface IClass<T> {
T SayHi(T msg);
}
泛型委托:
1. 定义泛型委托:使用 delegate
关键字和尖括号 <>
来定义泛型委托。
2. 实例化泛型委托:创建泛型委托的实例时,需要指定类型参数。
3.使用泛型委托:可以像使用普通委托一样使用泛型委托来调用方法。
常见的委托Action,Func,Predicate,Converter
Action,Action<in T>
委托:滞后性
Action特点:没有参数,没有返回值。只要没有参数,没有返回值的方法都可以满足委托。
namespace 练习
{
internal class Program
{
static void Main(string[] args)
{
//常见的委托 Action,Func,Predicate,Converter
//Action特点:没有参数,没有返回值。只要没有参数,没有返回值的方法都可以满足委托。
// ()=>{} 称为拉姆达表达式
Action action =() => {
Console.WriteLine("action委托");
};
// 必须调用,才会执行
action();
Action action1 = Mehtod1; // 切记:不能添加小括号。
action1();
//泛型委托
Action<int> action2 = (int arg) =>
{
Console.WriteLine(arg);
};
action2(22);
Action<string> action3 = Mehtod2;
action3("str-Hello world");
}
private static void Mehtod1()
{
Console.WriteLine("没有参数,没有返回值的方法,可以满足Action委托");
}
private static void Mehtod2(string str) {
Console.WriteLine(str);
}
}
}
泛型约束:
泛型约束是在使用泛型时限制可接受的类型,即对泛型参数进行一些限制,控制在适当范围内。
五种常见泛型约束:
值类型约束:要求泛型参数必须是值类型,例如int,short以及自定义的stuct等
如:public class MyClass2<T> where T : struct{}
引用类型约束:要求泛型参数必须是引用类型,例如string,object,class
如:public class MyClass<T> where T:class{}
构造函数约束:要求泛型参数必须有构造函数
如:public class MyClass3<T> where T : new(){}
接口约束:要求泛型参数必须实现某个接口
如:public class MyClass4<T> where T : System.IComparable{}
基类约束:要求泛型参数必须继承某个基类
如:public class MyClass5<T> where T : Customer{}