19.C# 泛型
1.泛型的概念
所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型。泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。
2. .net提供的泛型
2.1可空类型System.Nullable<T>
简单类型和引用类型的一个区别是简单类型必须包含一个值,引用类型可以是null,有时候需要把简单类型设置为可空类型,比如在处理数据库数据的时候。可以使用System.Nullable<T>声明一个可空类型,如System.Nullable<int> nullableInt = null;可以通过判断可空类型是否为null或者判断HasValue是否为true确定可空类型是否为空,如
System.Nullable<int> nullableInt = null;
if (nullableInt == null)
{
//
}
//等价于
if (nullableInt.HasValue)
{
//
}
可空类型缩写,因为可空类型比较常用,所以可以使用了一个比较简便的声明方式,可以在简单类型后面加个问号表示可以空类型,如int? op1=5
可空类型可以像简单类型一样使用+-*/等运算符来处理值,
int ? op1=
int ? op2=op1*;
简单类型可以隐式转换成可空类型,可空类型转换成简单类型必须使用显示转换..如下例子2会隐式转换成可空类型参与运算,运算结果强制转换成int类型。
int? op1=
int op2 =(int)op1*;
我们也可以通过可空类型的Value属性进行计算,但要判断是否有值,否则会引发异常
int? op1 = ;
if (op1.HasValue)
{
int op2 = op1.Value + ;
}
对于bool?之外的简单可空类型,如果运算式子中有一个为null,则计算结果为null
int? op1 = null;
int? op2 = op1 + ;//null
对于bool? 可以使用的运算符有&、|.他们的计算结果如下
op1 | op2 | op1&op2 | op1|op2 |
true | true | true | true |
true | false | false | true |
true | null | null | true |
false | true | false | true |
false | false | false | false |
false | null | false | null |
null | true | null | true |
null | false | false | null |
null | null | null | null |
可以这样简单的记忆,对于&运算符有一个false则为false,不管有没有null,其他情况有null皆为null;
对于|相反,有一个true则为true,不管有没有null,其他情况有null皆为null。
2.2空接运算符??
int ?op1=;
int ? op2=op1*?? 等价于
int ? op1=;
int ? op2=op1*==null?:op1*;
如果??前面的表达式的值为null,则赋予??后面表达式的值赋给变量,否则把??前面表达式的值赋值给变量
3..net提供的泛型
3.1 List<T>
使用List<T>可以很轻易的创建自己想要的强类型集合,它的很多方法都已经自动实现了,不必像之前那样从CollectionBase继承,并实现对应的方法。
创建T类型的对象需要以下语法,T是我们使用的具体类型,比如string。
List<T> myCollection=new List<T>();
List<T>有3个构造函数,我们可以使用默认的构造函数,也可以传一个支持IEnumerable接口的集合,或者传入一个list的初始容量。
public List()
public List(IEnumerable<T> collection)
public List(int capacity)
List<T>支持的方法和属性:
成员 | 说明 |
int Count | 返回集合中项的个数 |
void Add(T item ) | 把一个项添加到集合中 |
void AddRange(IEnumerable <T>) | 把多个项添加到集合中 |
IList<T> AsReadOnly | 给集合返回一个只读接口 |
int Capacity | 获取或者设置集合可以包含的项数。Capicity大于等于Count |
void Clear() | 清空所有项 |
bool Contains(T item) | 确定项item是否包含在集合中 |
void CopyTo(T [] array,int index) | 把集合复制到数组array中,从数组的索引index开始 |
IEnumerator<T>GetEnumerator() | 获取一个IEnumerator<T>实例用于foreach循环。注意:返回的接口是强类型化为T的,所以不需要类型转换。 |
int Indexof(T item) | 获取item的索引 |
int Insert(int index,T item) | 在index位置插入item项 |
bool Remove(T item) | 在集合中删除第一个item,并返回true,如果item不存在返回false |
void RemoveAt(int index) | 删除index位置的项 |
List<T>还有一个Item属性,允许List<T>像数组那样用下标进行访问
T item=myCollectionOfT[2];
List<T>例子
class Animal
{
public string name;
public Animal(string name)
{
this.name = name;
} public void Feed()
{
Console.WriteLine("{0} has been Feeded.", this.name);
}
}
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{ List<Animal> list = new List<Animal>();
Animal a = new Animal("Rock");
Animal b = new Animal("Peter");
Animal c = new Animal("Tom");
list.Add(a);
list.Add(b);
list.Add(c);
list.Add(a); foreach (Animal o in list)
{
o.Feed();
} Console.WriteLine(list.IndexOf(a));//返回第一个a的索引,如果集合未包含a返回-1 list.Remove(a);//删除第一个a
Console.WriteLine(list.IndexOf(a));
Console.WriteLine("删除后");
foreach (Animal o in list)
{
o.Feed();
} Console.WriteLine("Count:{0},Capacity:{1}", list.Count, list.Capacity);
Console.ReadLine();
}
}
3.2对泛型列表进行排序和搜索
对泛型列表进行排序和其他列表是一样的,其他列表,比如ArrayList可以通过IComparable、IComparer接口进行排序,泛型列表可以通过实现IComparable<T>,IComparer<T>进行排序,唯一的区别在于IComparable<T>,IComparer<T>是强类型化的,它只针对特定类型进行排序。下表列出了他们的区别:
泛型方法 | 非泛型方法 | 区别 |
int IComparable<T>.CompareTo(T otherObj) | int IComparable.CompareTo(object obj) | 泛型版本是强类型化的 |
int IComparer<T>.CompareTo(T otherObj) | int IComparer.CompareTo(object obj) | 泛型版本是强类型化的 |
//实现IComparable<Vector>的好处是比较时传入参数是强类型化的,不用类型转换
class Vector:IComparable,IComparable<Vector>
{
public double? R = null;//大小
public double? Theta = null;//方向,单位度 //弧度
public double? ThetaRadians
{
//度和弧度转换180度=pi弧度,
get
{
return (Theta * Math.PI / );
}
} public Vector(double? r, double? theta)
{
if (r < )
{
r = -r;
theta += ;
} Theta = theta % ;
R = r;
} public override string ToString()
{
string rString = R.HasValue ? R.ToString() : "null";
string thetaString = Theta.HasValue ? Theta.ToString() : "null";
return string.Format("{0},{1}",rString,thetaString);
} public int CompareTo(object obj)
{
if (this.R > ((Vector)obj).R)
return -;
else if (this.R < ((Vector)obj).R)
return ;
return ;
} public int CompareTo(Vector other)
{
if (this.R > other.R)
return ;
else if (this.R < other.R)
return -;
return ;
} } class Vectors:List<Vector>
{
public Vectors()
{ } public Vectors(IEnumerable<Vector> initialItem)
{
AddRange(initialItem);
}
} class VectorDelegates
{
public static int Compare(Vector x, Vector y)
{
if (x.R > y.R)
return ;
else if (x.R < y.R)
return -;
return ; } public static bool TopRightQuadrant(Vector target)
{
if (target.Theta >= 0.0 && target.Theta <= 90.0)
return true;
else
return false;
}
} class VectorComparer :IComparer,IComparer<Vector>
{
public static VectorComparer Default= new VectorComparer(); public int Compare(object x, object y)
{
return (int)(((Vector)x).Theta-((Vector)y).Theta);
} public int Compare(Vector x, Vector y)
{
return (int)(x.Theta-y.Theta);
}
} class Program
{ static void Main(string[] args)
{
Vectors route = new Vectors();
route.Add(new Vector(2.0, 90.0));
route.Add(new Vector(1.0, 180.0));
route.Add(new Vector(0.5, 45.0));
route.Add(new Vector(2.0, 90.0));
route.Add(new Vector(2.5, 315.0)); //使用Vector默认的比较方法排序,如果同时实现了IComparable,IComparable<Vector>两个接口,比较时优先使用强类型化的方法
//所以以下排序会使用public int CompareTo(Vector other)方法而不是public int CompareTo(object obj)排序
route.Sort(); Console.WriteLine("默认排序,按大小降序");
foreach (Vector v in route)
{
Console.WriteLine(v.ToString());
} Console.WriteLine("使用泛型委托排序,按大小升序");
Comparison<Vector> sorter = new Comparison<Vector>(VectorDelegates.Compare);
route.Sort(sorter);
foreach (Vector v in route)
{
Console.WriteLine(v.ToString());
} Console.WriteLine("使用IComparer<T>接口排序,按角度大小升序");
route.Sort(VectorComparer.Default);
foreach (Vector v in route)
{
Console.WriteLine(v.ToString());
} Console.WriteLine("恢复默认排序后使用IComparer<T>接口,对前面三项排序,按角度大小升序");
route.Sort();
route.Sort(, , VectorComparer.Default);
foreach (Vector v in route)
{
Console.WriteLine(v.ToString());
} Console.WriteLine("使用泛型委托Predicate<T>查找所有角度在0到90之间的向量");
Predicate<Vector> searcher = new Predicate<Vector>(VectorDelegates.TopRightQuadrant);
Vectors topRightQuadrant =new Vectors( route.FindAll(searcher));
foreach (Vector v in topRightQuadrant)
{
Console.WriteLine(v.ToString());
} route[].CompareTo(route[]); Console.ReadLine();
}
}
除了使用泛型接口IComparable<T>和IComparer<T>对泛型列表进行排序,上面的例子中还使用了泛型委托Comparison<T>对集合排序,它的定义如下
public delegate int Comparison<in T>(T x, T y);
它表示一个返回类型为int ,有两个T类型参数的委托,使用时要传入一个方法签名一样的方法
还可以使用泛型委托Predicate<in T>查找符合条件的项,它的定义如下
public delegate bool Predicate<in T>(T obj);
3.3Dictionary<K,V>
使用Dictionary<k,v>可以创建类型为k,和V的键值对集合,实例化之后可以像继承自DictionaryBase的类那样执行相同的操作。添加到接口Dictionary<k,v>的键必须唯一,否则会抛出ArgumentException异常。一般情况下,如果K是简单类型,比如int,string会判断值是否相等,如果是引用类型(除string)会判断两个对象的引用是否相等。如果要改变判断的规则可以给构造函数传入一个IEqualityComparer<TKey> comparer接口对象,如下使用不区分大小写的方法比较字符串
Dictionary<string, int> things = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
对于自己的类,我们可以继承IEqualityComparer<T>接口改变判断规则,IEqualityComparer<T>有两个比较方法,一般情况下先使用int GetHashCode(T obj)方法判断,如果两个比较对象返回值是相等的,则进一步用bool Equals(T x, T y)方法判断两个对象是否相等。
// 摘要:
// 定义方法以支持对象的相等比较。
//
// 类型参数:
// T:
// 要比较的对象的类型。
public interface IEqualityComparer<in T>
{
// 摘要:
// 确定指定的对象是否相等。
//
// 参数:
// x:
// 要比较的第一个类型为 T 的对象。
//
// y:
// 要比较的第二个类型为 T 的对象。
//
// 返回结果:
// 如果指定的对象相等,则为 true;否则为 false。
bool Equals(T x, T y);
//
// 摘要:
// 返回指定对象的哈希代码。
//
// 参数:
// obj:
// System.Object,将为其返回哈希代码。
//
// 返回结果:
// 指定对象的哈希代码。
//
// 异常:
// System.ArgumentNullException:
// obj 的类型为引用类型,obj 为 null。
int GetHashCode(T obj);
}
只用int GetHashCode(T obj)判断的情况,如下例子中,我们创建两个同名的Animal对象,但是这两个对象属于不同的引用,obj.GetHashCode()返回的是不同的值,所以不管Equals方法怎么实现,程序都会认为cow和cow2是两个不同的对象。
class Animal
{
protected string name;
public string Name
{
get { return name; }
set { name = value; }
} public Animal()
{
name = "The animal has no name.";
} public Animal(string newName)
{
name = newName;
} public void Feed()
{
Console.WriteLine("{0}has been fed.", name);
} } class AnimalComparer : IEqualityComparer<Animal>
{
public static AnimalComparer Default = new AnimalComparer(); public bool Equals(Animal x, Animal y)
{
if (Comparer.Default.Compare(x.Name, y.Name) == )
return true;
else
return false; //return x == y;
} public int GetHashCode(Animal obj)
{
return obj.GetHashCode();
}
} class Program
{ static void Main(string[] args)
{ Dictionary<Animal, int> a = new Dictionary<Animal, int>(AnimalComparer.Default);
Animal cow = new Cow("cow");
Animal cow2 = new Cow("cow"); a.Add(cow, );
a.Add(cow2, ); Console.ReadLine();
}
}
两个方法都使用的情况,我们把GetHashCode方法稍微改一下,同名的对象返回相同的值,此时调试发现,程序比较GetHashCode完方法之后又用Equals方法进行了比较,因为两次比较是相同的,所以添加cow2时程序报错
class Animal
{
protected string name;
public string Name
{
get { return name; }
set { name = value; }
} public Animal()
{
name = "The animal has no name.";
} public Animal(string newName)
{
name = newName;
} public void Feed()
{
Console.WriteLine("{0}has been fed.", name);
} } class AnimalComparer : IEqualityComparer<Animal>
{
public static AnimalComparer Default = new AnimalComparer(); public bool Equals(Animal x, Animal y)
{
if (Comparer.Default.Compare(x.Name, y.Name) == )
return true;
else
return false; //return x == y;
} public int GetHashCode(Animal obj)
{
return obj.Name.GetHashCode();
}
}
static void Main(string[] args)
{ Dictionary<Animal, int> a = new Dictionary<Animal, int>(AnimalComparer.Default);
Animal cow = new Animal("cow");
Animal cow2 = new Animal("cow"); a.Add(cow, );
a.Add(cow2, ); Console.ReadLine();
}
我们还可以给构造函数传递一个支持IDictionary<K,V>的集合,或者指定集合的大小Capacity,Dictionary<k,v>有以下构造函数,可以根据情况使用
可以使用集合的Keys和Values属性迭代集合中的键和值,也可以使用KeyValuePair<K,V>迭代集合中的每个项,使用Dictionary[K]访问对象某项的值
class Program
{ static void Main(string[] args)
{ Dictionary<Animal, int> a = new Dictionary<Animal, int>();
Animal cow = new Animal("cow");
Animal cow2 = new Animal("cow"); a.Add(cow, );
a.Add(cow2, ); foreach (Animal k in a.Keys)
{
Console.WriteLine(k.Name);
} foreach (int v in a.Values)
{
Console.WriteLine(v);
} foreach (KeyValuePair<Animal,int> d in a)
{
Console.WriteLine(d.Value);
} int value = a[cow];
Console.ReadLine();
}
}
19.C# 泛型的更多相关文章
- C++ Primer 学习笔记_45_STL实践与分析(19)--泛型算法的结构
STL实践与分析 --泛型算法的结构 引言: 正如全部的容器都建立在一致的设计模式上一样,算法也具有共同的设计基础. 算法最主要的性质是须要使用的迭代器种类.全部算法都指定了它的每一个迭代器形參可使用 ...
- 【C#4.0图解教程】笔记(第19章~第25章)
第19章 泛型 1.泛型概念 泛型提供了一种更准确地使用有一种以上的类型的代码的方式. 泛型允许我们声明类型参数化的代码,我们可以用不同的类型进行实例化. 泛型不是类型,而是类型的模板. 2.声明 ...
- lua迭代器和泛型for浅析
(一) 首要概念要理清: 1. 在lua中,函数是一种"第一类值",他们具有特定的词法域."第一类值"表示在lua中函数与其他传统类型的值(例如数字和字符串)具 ...
- Step By Step(Lua迭代器和泛型for)
Step By Step(Lua迭代器和泛型for) 1. 迭代器与Closure: 在Lua中,迭代器通常为函数,每调用一次函数,即返回集合中的"下一个"元素.每个迭代器都 ...
- C#温故知新:《C#图解教程》读书笔记系列
一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种 ...
- 完美C++(第5版)(双色)
完美C++(第5版)(双色) 薛正华 沈庚 韦远科 译 ISBN 978-7-121-23198-8 2014年6月出版 定价:148.00元 788页 16开 内容提要 <完美C++(第5版) ...
- .NET基础笔记(C#)
闲着没事就把以前学习时的笔记拿出来整理了一下,个人感觉有点用,就想拿出来跟园友共享一下.有些基础性的内容比如基本概念.语法什么的就不发了. 内容:1.构造方法(函数) 2.继承 3.访问修饰符 ...
- Javaweb学习笔记——(七)——————myexlipse基本使用、jdk5.0新特性及反射讲解
1.debug调试模式: *使用这种模式,调试程序(看到程序运行停止在这一行) -显示出来行号 -双击左边,出现一个圆点,表示设置了一个断点 *使用debug as方式,运行程序 -特使是否进入到调试 ...
- JavaWeb(HTML +css+js+Servlet....)
注意 1.不要把函数命名为add(),不然容易和自带的冲突报错 2.是createElement 不要把create中的e写掉了 3.记得是getElementsByTaxName和getElemen ...
随机推荐
- 利用ApplicationContextAware装配Bean
@Component public class SpringUtil implements ApplicationContextAware { private static ApplicationCo ...
- windows和linux换行规则的区别
在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符.但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符.要是在这 ...
- python数据类型——数据转换
数据类型有很多种,比如数值和字符,比如6和a,字符是需要加双引号的,下面的例子运行的结果是不一样的,数值会相加而字符会相连 print(6+6)print("6"+"6& ...
- 译:Dataiku 白皮书之《在银行和保险行业应用数据科学》
原文链接:Data Science For Banking & Insurance 如果不能正常访问,请点击备份获取. 在银行和保险行业应用数据科学 互联网巨头和金融技术创业时代的求生和发展 ...
- 执行Python出现LookupError: unknown encoding: cp65001解决办法
执行Python出现LookupError: unknown encoding: cp65001错误 dos下执行以下命令即可 chcp 以上.
- Metasploit学习记录---Nessus安装部署
1.Nessus介绍 nessus是目前世界上最为流行的漏洞扫描器之一.她提供完整的电脑漏洞扫描服务,并随时更新其漏洞数据库.Nessus不同于传统的漏洞扫描软件,可同时在本机或远端上遥控,进行系统的 ...
- 洛谷P1379 八数码难题
传送门 1.先用dfs枚举9!的全排列,存到hash数组里(类似离散化),因为顺序枚举,就不需要排序了 2.朴素bfs,判重就用二分找hash:如果发现当前状态=要求状态,输出步数结束程序 上代码 # ...
- ubantu16.04安装ns2.34 错误
把ns2.34解压缩之后,sudo ./install 出现的错误: 错误一:安装NS2.34过程中出现如下的错误:tools/ranvar.cc: In member function ‘virtu ...
- [jzoj]3875.【NOIP2014八校联考第4场第2试10.20】星球联盟(alliance)
Link https://jzoj.net/senior/#main/show/3875 Problem 在遥远的S星系中一共有N个星球,编号为1…N.其中的一些星球决定组成联盟,以方便相互间的交流. ...
- js高级的2
BOM0级事件元素绑定多个click最后只执行最后一个click. DOM2级事件元素绑定多个click,都要执行 注意当绑定的多个事件名,函数名,事件发生阶段三者完全一样时,才执行最后一个 div. ...