【C#】范型详解
借助泛型,你可以根据要处理的精确数据类型定制方法、类、结构或接口。 例如,不使用允许键和值为任意类型的 Hashtable 类,而使用 Dictionary<TKey,TValue> 泛型类并指定允许的密钥和值类型。 泛型的优点包括:代码的可重用性增加,类型安全性提高。
定义和使用泛型
泛型是为所存储或使用的一个或多个类型具有占位符(类型形参)的类、结构、接口和方法。 泛型集合类可以将类型形参用作其存储的对象类型的占位符;类型形参呈现为其字段的类型和其方法的参数类型。 泛型方法可将其类型形参用作其返回值的类型或用作其形参之一的类型。 以下代码举例说明了一个简单的泛型类定义。
public class Generic<T>
{
public T Field;
}
创建泛型类的实例时,指定用于替代类型形参的实际类型。 在类型形参出现的每一处位置用选定的类型进行替代,这会建立一个被称为构造泛型类的新泛型类。 你将得到根据你选择的类型而定制的类型安全类,如以下代码所示。
public static void Main()
{
Generic<string> g = new Generic<string>();
g.Field = "A string";
//...
Console.WriteLine("Generic.Field = \"{0}\"", g.Field);
Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
泛型术语
泛型类型定义 是用作模板的类、结构或接口声明,带有可包含或使用的类型的占位符。 例如, System.Collections.Generic.Dictionary<TKey,TValue> 类可以包含两种类型:密钥和值。 由于泛型类型定义只是一个模板,所以你无法创建作为泛型类型定义的类、结构或接口的实例。
泛型类型参数(或类型参数)是泛型类型或方法定义中的占位符。 System.Collections.Generic.Dictionary<TKey,TValue> 泛型类型具有两个类型形参 TKey 和 TValue,它们分别代表密钥和值的类型。
构造泛型类型(或 构造类型)是为泛型类型定义的泛型类型形参指定类型的结果。
泛型类型实参 是被泛型类型形参所替代的任何类型。
常见术语泛型类型包括构造类型和泛型类型定义。
借助泛型类型参数的协变和逆变,可以使用类型自变量的派生程度比目标构造类型更高(协变)或更低(逆变)的构造泛型类型。 协变和逆变统称为“变体” 。
约束是对泛型类型参数的限制。 例如,你可能会将一个类型形参限制为实现 System.Collections.Generic.IComparer 泛型接口的类型,以确保可对该类型的实例进行排序。 此外,你还可以将类型形参限制为具有特定基类、具有无参数构造函数或作为引用类型或值类型的类型。 泛型类型的用户不能替换不满足约束条件的类型实参。
泛型方法定义 是具有两个形参列表的方法:泛型类型形参列表和形参列表。 类型形参可作为返回类型或形参类型出现,如以下代码所示。
T Generic<T>(T arg)
{
T temp = arg;
//...
return temp;
}
泛型方法可出现在泛型或非泛型类型中。 值得注意的是,方法不会仅因为它属于泛型类型或甚至因为它有类型为封闭类型泛型参数的形参而成为泛型方法。 只有当方法有属于自己的类型形参列表时才是泛型方法。 在以下代码中,只有方法 G 是泛型方法。
class A
{
T G<T>(T arg)
{
T temp = arg;
//...
return temp;
}
}
class Generic<T>
{
T M(T arg)
{
T temp = arg;
//...
return temp;
}
}
泛型类的五大约束
- where T:struct 限定当前参数类型必须是值类型
- where T:class 限定当前类型参数类型必须是引用类型
- where T:new() 限定当前参数类型必须有一个无参构造器
- where T:<base class name>> 限定当前参数类型 必须是当前类 或者当前类为基类的类
- where T:<interface name>>限定当前参数类型必须实现指定接口
//泛型五大类型约束
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 泛型的五大约束
{
泛型类的五大约束
// 1. where T:struct 限定当前参数类型必须是值类型
// 2. where T:class 限定当前类型参数类型必须是引用类型
// 3. where T:new() 限定当前参数类型必须有一个无参构造器
// 4. where T:<base class name> 限定当前参数类型 必须是当前类 或者当前类为基类的类
//父类名
// 5. where T:<interface name>限定当前参数类型必须实现指定接口
class Program
{
static void Main( string [] args)
{
//D<Base> d = new D<Base>();
//Base ba = new Base();
//Console.WriteLine("泛型类D的 funcd的方法 在此被调用");
//d.funcd(ba);//此处调用funcd的方法
//C<Cb> c = new C<Cb>();
//c.f();
F f = new F ();
E < F > e = new E < F >();
e.funce(f);
}
}
public class Base
{
public void fb()
{
Console .WriteLine( "这是基类Base的方法fb" );
}
}
public class A < T > where T : struct
{
public void funca( T a)
{
}
}
public class B
{
}
public class C < T > where T : new ()
{
public void f()
{
Console .WriteLine( "这里是无参构造函数的方法" );
}
}
public class Cb
{
public Cb()
{
}
}
public class D < T > where T : Base
{
public void funcd( T a)
{
a.fb();
}
}
public interface INter
{
int funinter();
}
public class F : INter
{
public int funinter()
{
Console .WriteLine( "这里是实现了继承接口内的方法" );
return 0;
}
}
public class E < T > where T : INter // 第五种类型
{
public T funce( T a)
{
a.funinter();
Console .WriteLine( "这里是E内的方法的实现" );
return a;
}
}
}
泛型的利与弊
- 类型安全。 泛型将类型安全的负担从你那里转移到编译器。 没有必要编写代码来测试正确的数据类型,因为它会在编译时强制执行。降低了强制类型转换的必要性和运行时错误的可能性。
- 代码更少且可以更轻松地重用代码。 无需从基类型继承,无需重写成员。 例如,可立即使用 LinkedList 。
例如,你可以使用下列变量声明来创建字符串的链接列表:
LinkedList<string> llist = new LinkedList<string>();
- 性能更好。 泛型集合类型通常能更好地存储和操作值类型,因为无需对值类型进行装箱。
- 泛型委托可以在无需创建多个委托类的情况下进行类型安全的回调。 例如, Predicate
泛型委托允许你创建一种为特定类型实现你自己的搜索标准的方法并将你的方法与 Array 类型比如 Find、 FindLast和 FindAll方法一起使用。 - 泛型简化动态生成的代码。 使用具有动态生成的代码的泛型时,无需生成类型。
这会增加方案数量,在这些方案中你可以使用轻量动态方法而非生成整个程序集。
以下是泛型的一些局限:
- 泛型类型可从多数基类中派生,如 MarshalByRefObject (约束可用于要求泛型类型形参派生自诸如
MarshalByRefObject的基类)。 不过,.NET 不支持上下文绑定的泛型类型。 泛型类型可派生自
ContextBoundObject,但尝试创建该类型实例会导致 TypeLoadException。 - 枚举不能具有泛型类型形参。 枚举偶尔可为泛型(例如,因为它嵌套在被定义使用 Visual Basic、C# 或 C++ 的泛型类型中)。
- 轻量动态方法不能是泛型。
- 在 Visual Basic、C# 和 C++ 中,包含在泛型类型中的嵌套类型不能被实例化,除非已将类型分配给所有封闭类型的类型形参。 另一种说法是:在反射中,定义使用这些语言的嵌套类型包括其所有封闭类型的类型形参。 这使封闭类型的类型形参可在嵌套类型的成员定义中使用。
引用
相关文章
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
- 请求大神,C#如何截取字符串中指定字符之间的部分 按指定字符串分割 一分为二 c# 去除字符串中的某个已知字符
- ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借
- 史上最全的CSS hack方式一览 jQuery 图片轮播的代码分离 JQuery中的动画 C#中Trim()、TrimStart()、TrimEnd()的用法 marquee 标签的使用详情 js鼠标事件 js添加遮罩层 页面上通过地址栏传值时出现乱码的两种解决方法 ref和out的区别在c#中 总结
- c#实例化继承类,必须对被继承类的程序集做引用 .net core Redis分布式缓存客户端实现逻辑分析及示例demo 数据库笔记之索引和事务 centos 7下安装python 3.6笔记 你大波哥~ C#开源框架(转载) JSON C# Class Generator ---由json字符串生成C#实体类的工具
- 装饰者模式的学习(c#) EF SaveChanges() 报错(转载) C# 四舍五入 保留两位小数(转载) DataGridView样式生成器使用说明 MSSQL如何将查询结果拼接成字符串 快递查询 C# 通过smtp直接发送邮件 C# 带参访问接口,WebClient方式 C# 发送手机短信 文件 日志 写入 与读取
- c#之泛型详解
- C# IL DASM 使用-破解c#软件方法
- Word控件Spire.Doc 【文档操作】教程(六):在 C#、VB.NET 中计算文档中的单词数、更改语言词典
- C#,图像二值化(07)——全局阈值的迭代算法(Iteration Thresholding)及其源代码
- c# Invoke和BeginInvoke 区别详解
- C# DataTable用法详解
- c#中禁用当鼠标位于控件上方时ToolStripDropDownButton上的蓝色焦点
- C# Regex类详解
- C# 中xml数组的序列和反序列化方法
- 《C#初学者指南》一导读
- 一种非常巧妙的读取串口数据的方法--C#
- C# 中的MDI 窗体
- C# 解决约瑟夫环问题
- C# 字符串操作详解
- c#事件使用示例详解
- c#-中如何退出程序后自动重新启动程序
- C#基础 字段、属性、变量三者的关系