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=5 int ? op2=op1*8;
简单类型可以隐式转换成可空类型,可空类型转换成简单类型必须使用显示转换..如下例子2会隐式转换成可空类型参与运算,运算结果强制转换成int类型。
int? op1=5 int op2 =(int)op1*2;
我们也可以通过可空类型的Value属性进行计算,但要判断是否有值,否则会引发异常
int? op1 = 5; if (op1.HasValue) { int op2 = op1.Value + 1; }
对于bool?之外的简单可空类型,如果运算式子中有一个为null,则计算结果为null
int? op1 = null; int? op2 = op1 + 1;//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=2; int ? op2=op1*2??5 等价于 int ? op1=2; int ? op2=op1*2==null?5:op1*2;
如果??前面的表达式的值为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 / 180); } } public Vector(double? r, double? theta) { if (r < 0) { r = -r; theta += 180; } Theta = theta % 360; 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 -1; else if (this.R < ((Vector)obj).R) return 1; return 0; } public int CompareTo(Vector other) { if (this.R > other.R) return 1; else if (this.R < other.R) return -1; return 0; } } 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 1; else if (x.R < y.R) return -1; return 0; } 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(0, 3, 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[0].CompareTo(route[1]); 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) == 0) 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, 1); a.Add(cow2, 1); 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) == 0) 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, 1); a.Add(cow2, 1); 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, 1); a.Add(cow2, 1); 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(); } }