c#2解决c#1中的问题之用泛型实现参数化类型
为什么需要泛型
你手中还有c#1的代码吗?数一数其中的强制转换有多少,特别是那些大量使用集合的代码。几乎每次使用foreach都需要隐式的强制转换。使用那些为不同数据类型而设计的类型,就意味着强制转换。他们平静的告诉编译器:什么都别担心,一切都很正常,把那个表达式看做似乎它具有这种特性就可以了。任何api只要将object作为参数类型或返回类型使用,就可能在某个时候涉及强制类型转换。设计只有一个类,并将object作为根的层次结构,将使一切都变的更加简单。但是object类型本身是及其愚钝的一个存在。要用一个object做真正有意义的事情,几乎都要对它进行强制类型转换。
强制类型转换很糟糕吧。它并不是“永远不用”的那种糟糕,而是不得不用的那种糟糕。但是,如果要想达到某个目的就不得不用,那它就是坏的。发生强制类型转换后,就意味着你本来应该为编译器提供更多信息,但你选择的是让编译器在编译时相信你,并生成一个检查,以便执行时运行,以验证你所言非虚。
如果需要在某处将某些信息传达给编译器,那么正在读你代码的人也极有可能需要相同的信息。当然,他们能在你进行强制转换的时候读到这些信息,但那几乎没有任何用处。保存这些信息的理想位置是声明变量或方法的位置。如果提供一个其他人不需要访问源代码就能调用的类型或方法,这一点就更加重要。有了泛型,用户在程序中用错误的参数调用库时,就无法通过编译。
以上这些足以证明泛型存在的价值。但泛型还改善了性能。首先,由于编译器能执行更多的检查,所以执行时的检查就可以少做,其次,JIT非常聪明地处理值类型,能消除许多情况下的装箱和拆箱处理。某些情况下,无论在速度还是内存的消耗上,有泛型和没有泛型的结果会大相径庭。
泛型所带来的好处非常像静态语言较之动态语言的优点:更好的编译时检查,更多的在代码中能表现的信息,更多的IDE支持,更好的性能。
日常使用的简单泛型
泛型字典
用Dictionary<TKey,TValue>统计文本中的单词数
static Dictionary<string,int> CountWords(string text) { Dictionary<string,int> frequencies; frequencies=new Dictionary<string,int>(); string[] words=Regex.Split(text,@"\W+"); foreach(string word in words) { if(frequencies.ContaninsKey(word)) { frequencies[word]++; } else { frequencies[word]=; } return frequencies; } ... string text=@"Do you like green eggs and ham? I do not like them, Sam-I-am. I do not like green eggs and ham."; Dictionary<string,int> frequencies=CountWords(text); foreach(KeyValuePair<string,int>) { string word=entry.Key; int frequency=entry.Value; Console.WriteLine(word+value); }
泛型字典
泛型类型和类型参数
有两种形式的泛型:泛型类型(包括类、接口、委托和结构)和泛型方法。
类型参数是真实类型的占位符。在泛型声明中,类型参数要放在一对尖括号中,并以逗号分开。使用泛型类型或者方法时,要用真实的类型代替。这些真实的类型称为类型实参。
如果没有为任何类型参数提供类型实参,声明的就是一个未绑定泛型类型。如果制定了类型实参,该类型就称为一个已构造类型。类型可以看做是对象的蓝图。未绑定泛型类型是是已构造类型的蓝图。它是一种额外的抽象层。如下图所示
已构造类型可以是开放或封闭的。开放类型还包含一个类型参数,而封闭类型则不是开放的,类型的每个部分都是明确的。所有代码实际都是在一个封闭的已构造类型的上下文中执行。
类型参数“接收”信息,类型实参“提供”信息,这个思路与方法参数/方法实参是一样的。只不过类型实参必须为类型,而不能为任意的值。只有在编译时才能知道类型实参的类型,它可以是(或包含)相关上下文中的类型参数。
可以认为“封闭类型”拥有“开放类型”的API,只不过类型参数被替换成了对应的类型实参。
以下是Dictionary<TKey,TValue>可能的代码,虽然没有包括任何实际的方法实现,而且实际的成员数量比这更多。
namespace System.Collections.Generic
{
public class Dictionary<TKey,TValue>:IEnumerable<KeyValuePair<TKey,TValue>>
{
public Dictionary(){.....}
pubic void Add(TKey key,TValue){....}
public TValue this[TKey key]
{
get{...}
set{...}
}
public bool ContainsValue(TValue value){...}
public bool ContainsKey(TKey key){...}
{...other members...}
}
}
注意Dictionary<TKey,TValue>是如何实现泛型接口IEnumerable<KeyValuePaie<TKey,TValue>>的(事实上还实现了其他许多接口)。为类指定任何类型的实参都可以应用到具有相同类型的接口中。所以,在我们的例子中,Dictionary<sring,int>会实现IEnumerable<KeyValuePair<string,int>>.后者实际是某种“双重泛型”结构。正是由于实现了IEnumerable<KeyValuePair<string,int>>接口,上例子中才能像那样枚举keys和values.
还值得指出的是,构造函数不在尖括号中列出类型参数。类型参数从属于类型,而非从属于某个特定的构造函数,所以才会在声明类型的时候声明。成员(仅限方法)仅在引入新类型参数时才需要声明。
泛型可以重载,只需改变一下类型参数的数量就可以了。
泛型方法和判读泛型声明
我们已经习惯于方法的参数和返回值有固定的类型,而且已看到了泛型类型如何在它的方法声明中使用类型参数。泛型方法则更进一步—即使你已经确切地知道要操作哪一个已构造类型,泛型方法也可以有类型参数。
虽然Dictionary<TKey,TValue>没有任何泛型方法,但它的近亲List<T>是有的。你能想的到,List<T>表示的是由任意指定类型的数据项构成的一个列表。先记住这一点:T是在整个类的范围内使用的类型参数。
泛型方法实例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test泛型
{
class Program
{
static Guid getGuid(string x)
{
System.Guid guid = System.Guid.NewGuid();
return guid;
}
static void Main(string[] args)
{
List<string> stringers = new List<string>();//创建一个字符串列表
stringers.Add("hello");
stringers.Add("word");
Converter<string, Guid> converter = getGuid;//创建委托实例
List<Guid> guids;
guids=stringers.ConvertAll<Guid>(converter);//调用委托
foreach(Guid g in guids)
{
Console.WriteLine(g.ToString());
}
}
}
}
运行结果:
在非泛型类型中实现泛型方法
非泛型类型也可以拥有泛型方法
实例:
static List<T> MakeList<T> (T first ,T second)
{
List<T> list=new List<T>;
list.Add(first);
list.Add(second);
return list;
}
....
List<string> list=MakeList<string>("Line 1","Line 2");
foreach(string x in list)
{
Console.WriteLine(x);
}
c#2解决c#1中的问题之用泛型实现参数化类型的更多相关文章
- 解决vi/vim中粘贴会在行首多很多缩进和空格的问题
解决vi/vim中粘贴会在行首多很多缩进和空格的问题 secureCRT会将你原来的文本原封不动的按照字符串的样式发送给服务器.所以当你的服务器上的vim设置为autoindent的话,在i模式下,那 ...
- 解决spring配置中的bean类型的问题:BeanNotOfRequiredTypeException
解决spring配置中的bean类型的问题:BeanNotOfRequiredTypeException这个问题出现的原因:一般在使用annotation的方式注入spring的bean 出现的,具体 ...
- 解决关于jquery中$.get()方法总是报“HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy”错的方法
解决关于jquery中$.get()方法总是报“HierarchyRequestError: Node cannot be inserted at the specified point in the ...
- ASP.NET MVC 解决LINQ表达式中的SqlMethods 未找到命名空间问题
右键项目属性下的引用: 添加引用: 搜索寻找——System.Data.Linq,然后添加成功,即可解决LINQ表达式中的SqlMethods 未找到命名空间问题
- 解决比较Oracle中CLOB字段问题
解决比较Oracle中CLOB字段问题 Oracle中CLOB和BLOB字段虽说在开发中满足了存放超大内容的要求,但是在一些简单使用中确频频带来麻烦.CLOB中存放的是指针,并不能直接取到实际值. ...
- VIM: 解决vi/vim中粘贴时行首出现很多缩进和空格的问题
解决vi/vim中粘贴时行首出现很多缩进和空格的问题 http://www.jbxue.com/LINUXjishu/12232.html 由于在secureCRT中会将原来的文本原封不动的按照字符串 ...
- (转)解决jdk1.8中发送邮件失败(handshake_failure)问题
解决jdk1.8中发送邮件失败(handshake_failure)问题 作者 zhisheng_tian 2016.08.12 22:44* 字数 1573 阅读 2818评论 6喜欢 9 暑假在家 ...
- dgraph解决社交关系中的正反向查找
dgraph解决社交关系中的正反向查找 本篇介绍的是, 社交关系中的关注者与被关注者在dgraph中如何实现查找. 对dgraph的基本操作不太清楚的可以看看我之前写的博客 dgraph实现基本操作 ...
- 解决java web中safari浏览器下载后文件中文乱码问题
解决java web中safari浏览器下载后文件中文乱码问题 String fileName = "测试文件.doc"; String userAgent = request.g ...
随机推荐
- 功能间(两个form)数据交互的编程方法
功能间数据交互的编程方法 现在框架具有在两个打开的功能之间进行通讯的机制.通讯是指,一个功能调用另外一个功能的方法,或者传递一些数据,并得到返回结果.比如处置单打开结算单,结算单保存后,将结算单号反填 ...
- SED修改指定行
一个文件:cat aa #如果第三行是5的话将改为8,很明显第三行是5所以 结果改变 [root@remote ~]# sed -e '3s/5/8/' aa [root@remote ~]# #如果 ...
- 【转】java--final
1.final数据 许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”.常数主要应用于下述两个方面: (1) 编译期常数,它永远不会改变 (2) 在运行期初始化的一个值,我们不希望它发生变化 ...
- Android Studio创建库项目及引用
Android Studio创建库项目其实创建的是在主项目下创建Module模块,这个Module模块创建的时候选择库项目模式. 为什么要这样处理呢?因为在Android Studio中一个WorkS ...
- Windows8下通过IPv4地址访问Tomcat
最近在做Android开发,手机客户端需要通过IPv4地址访问电脑启动的Web应用服务. 在Windows 7不需要做什么设置,localhost,127.0.0.1或者192.168.0.100都可 ...
- Solr搜索结果说明
在admin页面,输入相关内容后,会返回xml格式的内容.说明如下: [html] view plaincopy <?xml version="1.0" encoding=& ...
- 百度地图api基本用法
首先 ,如果想调用百度地图api,你需要获取一个百度地图api的密钥. 申请密钥很简单,在百度地图api的首页就有相关链接,填写相关信息百度就会给你一个密钥了. 接下来,就是引入百度地图的api 关键 ...
- ActiveMQ持久化方式(转)
消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息 中心重新启动后仍然可以将消息发送出去,如果把这种持久化 ...
- AngularJS之WebAPi上传
AngularJS之WebAPi上传(十) 前言 前面一系列我们纯粹是讲AngularJS,在讲一门知识时我们应该结合之前所学综合起来来做一个小的例子,前面我们讲了在MVC中上传文件的例子,在本节 ...
- Delphi反射
最近在写一个框架,需要用到反射,与C# java这些原生支持反射的语言不同,delphi对反射的支持相对要弱一些,但也够用了,其实C#的大部分的思想还是从 delphi而来,毕竟都是安德鲁斯的杰作. ...