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();
}
}