c#之泛型详解(Generic)
这篇文章主要来讲讲c#中的泛型,因为泛型在c#中有很重要的位置,对于写出高可读性,高性能的代码有着关键的作用。
一、什么是泛型?
泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个非常重要的新功能。
我们在编程程序时,经常会遇到功能非常相似的模块,只是它们处理的数据不一样。但我们没有办法,只能分别写多个方法来处理不同的数据类型。这个时候,那么问题来了,有没有一种办法,用同一个方法来处理传入不同种类型参数的办法呢?泛型的出现就是专门来解决这个问题的,可以看出,微软还是很贴心的。
二、为什么要使用泛型?
接下来我们来看一段代码。
public class GenericClass
{
public void ShowInt(int n)
{
Console.WriteLine("ShowInt print {0},ShowInt Parament Type Is {1}",n,n.GetType());
}
public void ShowDateTime(DateTime dt)
{
Console.WriteLine("ShowDateTime print {0},ShowDateTime Parament Type Is {1}", dt, dt.GetType());
}
public void ShowPeople(People people)
{
Console.WriteLine("ShowPeople print {0},ShowPeople Parament Type Is {1}", people, people.GetType());
}
}
static void Main(string[] args)
{
GenericClass generice = new GenericClass();
generice.ShowInt();
generice.ShowDateTime(DateTime.Now);
generice.ShowPeople(new People { Id = , Name = "Tom" }); Console.ReadKey();
}
显示结果:

我们可以看出这三个方法,除了传入的参数不同外,其里面实现的功能都是一样的。在1.1版的时候,还没有泛型这个概念,那么怎么办呢。就有人想到了OOP三大特性之一的继承,我们知道,C#语言中,所有类型都源自同一个类型,那就是object。
public class GenericClass
{
public void ShowObj(object obj)
{
Console.WriteLine("ShowObj print {0},ShowObj Parament Type Is {1}", obj, obj.GetType());
}
}
static void Main(string[] args)
{
Console.WriteLine("*****************object调用*********************");
generice.ShowObj();
generice.ShowObj(DateTime.Now);
generice.ShowObj(new People { Id = , Name = "Tom" }); Console.ReadKey();
}
显示结果:

我们可以看出,目地是达到了。解决了代码的可读性,但是这样又有个不好的地方了,我们这样做实际上是一个装箱拆箱操作,会损耗性能。
终于,微软在2.0的时候发布了泛型。接下来我们用泛型方法来实现该功能。
三、泛型类型参数
在使用泛型方法之前,我们先来了解下有关于泛型的一些知识。
在泛型类型或方法定义中,类型参数是在其实例化泛型类型的一个变量时,客户端指定的特定类型的占位符。 泛型类( GenericList<T>)无法按原样使用,因为它不是真正的类型;它更像是类型的蓝图。 若要使用 GenericList<T>,客户端代码必须通过指定尖括号内的类型参数来声明并实例化构造类型。 此特定类的类型参数可以是编译器可识别的任何类型。 可创建任意数量的构造类型实例,其中每个使用不同的类型参数,如下所示:
GenericList<float> list1 = new GenericList<float>();
GenericList<ExampleClass> list2 = new GenericList<ExampleClass>();
GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();
在 GenericList<T> 的每个实例中,类中出现的每个 T 在运行时均会被替换为类型参数。 通过这种替换,我们已通过使用单个类定义创建了三个单独的类型安全的有效对象。
三、泛型约束
定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的几种类型施加限制。 如果客户端代码尝试使用约束所不允许的类型来实例化类,则会产生编译时错误。 这些限制称为约束。 通过使用 where 上下文关键字指定约束。 下表列出了六种类型的约束:

where T:结构(类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。)
class MyClass<U>
where U : struct///约束U参数必须为“值 类型”
{ } public void MyMetod<T>(T t)
where T : struct
{
}
where T:类(类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。)
class MyClass<U>
where U : class///约束U参数必须为“引用类型”
{ } public void MyMetod<T>(T t)
where T : class
{
}
where T:new()(类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。)
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
// ...
}
where T:<基类名>(类型参数必须是指定的基类或派生自指定的基类。)
public class Employee{}
public class GenericList<T> where T : Employee
where T:<接口名称>(类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。)
/// <summary>
/// 接口
/// </summary>
interface IMyInterface
{
} /// <summary>
/// 定义的一个字典类型
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TVal"></typeparam>
class Dictionary<TKey, TVal>
where TKey : IComparable, IEnumerable
where TVal : IMyInterface
{
public void Add(TKey key, TVal val)
{
}
}
where T:U(为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。也就是说T和U的参数必须一样)
class List<T>
{
void Add<U>(List<U> items) where U : T {/*...*/}
}
以上就是对六种泛型的简单示例,当然泛型约束不仅仅适用于类,接口,对于泛型方法,泛型委托都同样适用。
三、泛型方法
public class GenericClass
{
public void ShowT<T>(T t)
{
Console.WriteLine("ShowT print {0},ShowT Parament Type Is {1}", t, t.GetType());
}
}
static void Main(string[] args)
{
Console.WriteLine("*****************泛型方法调用*********************");
generice.ShowT<int>();
generice.ShowT<DateTime>(DateTime.Now);
generice.ShowT<People>(new People { Id = , Name = "Tom" }); Console.ReadKey();
}
显示结果:

也是一样的,现在终于实现了我们想要达到的效果了。我们可以看出,无论是什么方式调用,最后我们获取出来的类型都是原始类型。我们知道,用object获取是利用了继承这一特性,当编译器编译的时候,我们传入的参数会进行装箱操作,当我们获取的时候又要进行拆箱操作,这个方法会损耗性能 。那么泛型方法实现的原理又是怎样的呢?首先,我们要知道,泛型是一个语法糖,在我们调用泛型方法,编译器进行编译时,才会确定传入的参数的类型,从而生成副本方法。这个副本方法与原始方法一法,所以不会有装箱拆箱操作,也就没有损耗性能这回事了。
四、泛型类
泛型类封装不特定于特定数据类型的操作。
通常,创建泛型类是从现有具体类开始,然后每次逐个将类型更改为类型参数,直到泛化和可用性达到最佳平衡。
创建自己的泛型类时,需要考虑以下重要注意事项:
- 要将哪些类型泛化为类型参数。
通常,可参数化的类型越多,代码就越灵活、其可重用性就越高。 但过度泛化会造成其他开发人员难以阅读或理解代码。
- 要将何种约束(如有)应用到类型参数
其中一个有用的规则是,应用最大程度的约束,同时仍可处理必须处理的类型。 例如,如果知道泛型类仅用于引用类型,则请应用类约束。 这可防止将类意外用于值类型,并 使你可在 T 上使用 as 运算符和检查 null 值。
是否将泛型行为分解为基类和子类。
因为泛型类可用作基类,所以非泛型类的相同设计注意事项在此也适用。 请参阅本主题后文有关从泛型基类继承的规则。
实现一个泛型接口还是多个泛型接口。
class BaseNode { }
class BaseNodeGeneric<T> { }
// concrete type
class NodeConcrete<T> : BaseNode { }
//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }
//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { }
五、泛型接口
- 定义一个泛型接口:
interface IMyGenericInterface<T>
{
}
- 一个接口可定义多个类型参数,如下所示:
interface IMyGenericInterface<TKey,TValue>
{
}
- 具体类可实现封闭式构造接口,如下所示:
interface IBaseInterface<T> { }
class SampleClass : IBaseInterface<string> { }//如果T有约束,那么string类型必须得满足T的约束
六、泛型委托
委托可以定义它自己的类型参数。 引用泛型委托的代码可以指定类型参数以创建封闭式构造类型,就像实例化泛型类或调用泛型方法一样,如以下示例中所示:
class Program
{
static void Main(string[] args)
{
Del<int> m1 = new Del<int>(Notify);
m1.Invoke();
Del<string> m2 = new Del<string>(Notify);
m2.Invoke("字符串"); Console.ReadKey();
} public delegate void Del<T>(T item);
public static void Notify(int i) { Console.WriteLine("{0} type is {1}", i,i.GetType()); }
public static void Notify(string str) { Console.WriteLine("{0} type is {1}", str, str.GetType()); } }
运行结果:

七、泛型代码中的默认关键字:Default
在泛型类和泛型方法中产生的一个问题是,在预先未知以下情况时,如何将默认值分配给参数化类型 T:
T 是引用类型还是值类型。
如果 T 为值类型,则它是数值还是结构。
给定参数化类型 T 的一个变量 t,只有当 T 为引用类型时,语句 t = null 才有效;只有当 T 为数值类型而不是结构时,语句 t = 0 才能正常使用。解决方案是使用 default 关键字,此关键字对于引用类型会返回空,对于数值类型会返回零。对于结构,此关键字将返回初始化为零或空的每个结构成员,具体取决于这些结构是值类型还是引用类型。
namespace MyGeneric
{
class Program
{
static void Main(string[] args)
{
object obj1=GenericToDefault<string>();
object obj2 = GenericToDefault<int>();
object obj3 = GenericToDefault<StructDemo>();
Console.ReadKey();
}
public static T GenericToDefault<T>()
{
return default(T);
}
}
public struct StructDemo
{
public int Id { get; set; }
public string Name { get; set; }
}
}
运行结果:



c#之泛型详解(Generic)的更多相关文章
- java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...
- 【转】java 泛型详解
java 泛型详解 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 ...
- 【转载】Java泛型详解
[转载]http://www.importnew.com/24029.html 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考 ...
- java 泛型详解(普通泛型、 通配符、 泛型接口)
java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...
- Java泛型详解(转)
文章转自 importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...
- Java的泛型详解(一)
Java的泛型详解 泛型的好处 编写的代码可以被不同类型的对象所重用. 因为上面的一个优点,泛型也可以减少代码的编写. 泛型的使用 简单泛型类 public class Pair<T> { ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- Java基础11:Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 本文参考https://blog.csdn.net/s10461/article/details/53941091 具体代码在我的GitHub中可以找 ...
- java 泛型详解-绝对是对泛型方法讲解
Reference: http://blog.csdn.net/s10461/article/details/53941091 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模 ...
随机推荐
- python--深浅拷贝 join() 列表和字典的删除 fromkeys建立字典
北京的冬天雾霾依旧很重,依稀记得人生初见雾霾时的样子,那时的回忆也是有些尴尬,不过雾霾也伴随了我的成长,成为了我肺泡中不可分割的一部分. 今天我想写的是拷贝的问题,不过在这之前我想先补充一点关于字符串 ...
- WebService-php- 2(17)
wsdl实例 <?xml version ='1.0' encoding ='UTF-8' ?> <definitions targetNamespace='http://local ...
- Python 开发安卓Android及IOS应用库Kivy安装尝试
Python 开发安卓Android及IOS应用库Kivy安装尝试: 先来看看这货可以用来制作什么应用: Create a package for Windows Create a package f ...
- 老调重弹-access注入过主机卫
本文作者:i春秋签约作家——非主流 大家好,我是来自农村的非主流,今天就给在座的各位表演个绝活. 首先打开服务器上安装了主机卫士的网站. 尝试在变量id的值后面插入万恶的单引号,根据报错,我们可以分析 ...
- [转] Foobar2000 DSP音效外挂元件-Part4
[转] Foobar2000 DSP音效外挂元件-Part4 在第1部分的文章里主要介绍了foobar2000预设的DSP音效调整,这些则示要介绍几个比较会用到的DSP外挂元件,在foobar2000 ...
- 题目1005:Graduate Admission(结构体排序)
问题来源 http://ac.jobdu.com/problem.php?pid=1005 问题描述 这道题理解题意有些麻烦,多看几遍先理解题意再说.每个学生有自己的三个成绩,一个编号,以及一个志愿列 ...
- centos 7 网站前端中文乱码分析、解决办法
2019-03-28 1.网站前端中文文字乱码主要原因有两点: (1)mysql数据库内部存储的数据本身处于乱码状态 (2)前端与数据库传输数据的字符集与数据库内部字符集不一致导致 2.查找造成中文乱 ...
- Google发布移动网站设计原则
Google 刚刚发布了由 Google 与 AnswerLab 联合打造,名为<Principles of Mobile Site Design: Delight Users and Driv ...
- mongodb启动失败:child process failed, exited with error number 100
参考 http://www.dataguru.cn/thread-107361-1-1.html 里面的路径 根据自己的--dbpath的路径 和l--logpath路径去找
- 重签名android测试包
我的一个例子:jarsigner -digestalgSHA1 -sigalg MD5withRSA -keystore C:\Users\sunyang\.android\debug.keystor ...