为了满足灵活的数据结构,需要生成一种动态伸缩或只保存某个标准的对象。
集合类可划分为两大类:
非泛型集合(主要位于System.Collections命名空间) 通常设计为操作System.Object类型,因此是非常松散类型的容器。
泛型集合(主要位于System.Collections.Generic)
装箱可以正式定义为:显示的将值类型分配给System.Object变量的过程。
拆箱:把保存在对象引用中的值转换回栈上的相应值类型,从语法上来讲,拆箱操作更像是转换操作。
装箱拆箱的步骤:
1.必须在托管堆上分配一个新对象;
2.基于栈数据的值必须被转移到新分配的内存位置;
3.在拆箱时,保存在堆对象中的值必须转移会栈;
4.堆上无用的对象最后总会被回收。
static void Express()
{
//在传递给要求object的成员时,值类型将自动装箱
ArrayList myInts = new ArrayList();
myInts.Add(10);
myInts.Add(20);
myInts.Add(30);
//当将Object转换回栈数据时,会发生拆箱
int i = (int)myInts[0];
//由于WriteLine()要求Object类型,因此会再次发生自动装箱
Console.writeline("Value of your int:{0}",i);
}
从这个例子中我们可以想象如果我们只是本地变量本地保存,我们再去确认变量的类型就变很难,
而且,装箱与拆箱伴随着代码性能的消耗,因而泛型的出现很好的解决了这一问题
泛型的优势:
1.泛型提供了更好的性功能,因为他们不会导致装箱火柴想的损耗;
2.泛型更类型安全,因为他们只包含我们制定的类型;
3.泛型大幅减少了构建自定义集合类型的需要,因为当创建泛型容器时指定了“类型的类型”。
static void UseGenericList()
{
//该List<>只能容纳Person对象
List<Person> morePersons = new List<Person>();
morePersons.Add(new Person('safs','ming',24));
writeline(morePersons[0]);
//List<> Object
List<Int> moreInts = new List<List>();
moreInts.Add(10);
moreInts.Add(20);
int sum = moreInts[0]+moreInts[1];
//it`s will error ,type isn`t right
moreInts.Add(new Person('string<> ','string<>',int));
}
泛型参数的作用(只有类、结构、接口和委托可以使用泛型,枚举类型不可以)
IEnumerable ,符号读作of T。通俗称为占位符。类型参数(占位符)的名称可以随意取,它取决于创建该泛型项的人。
通常情况下,T表示类型,TKey或K表示键,TVaue或V表示值。
1.为泛型类/结构指定类型参数
public class List<T>:IList<T>,ICollection<T>,IEnumerable<T>,IReadOnlyList<T>,IList,ICollection,IEnumerable
{
public void Add(T item);
public ReadOnlyCollection<T> AsReadOnly();
public int BinarySearch(T item);
.....
}
当我们创建一个指定person对象的List时,就会执行将上面代码中所有的T参数转换为Person;
public class List<Person>:IList<Person>,ICollection<Person>,IEnumerable<Person>,IReadOnlyList<Person>,IList,ICollection,IEnumerable
{
public void Add(Person item);
public ReadOnlyCollection<Person> AsReadOnly();
public int BinarySearch(Person item);
.....
}
2.为泛型成员指定类型参数:
int[] myInts = {10,4,2,33,52}
//为Sort<>()泛型方法指定占位符
Array.Sort<int>(myInts);
foreach (int i in myInts)
{
Console.WriteLine(i);
}
3.为泛型接口指定类型参数
public interface IComparable<Car>
{
int CompareTO(T obj);
}
public class Car :IComparable<Car>
{
...
//实现IComparable<T>
int IComparable<Car>.CompareTo(Car obj)
{
if(this.CarID > obj.CarID)
return 1;
if(this.CarID > obj.CarID)
return -1;
else
return 0;
}
}
集合初始化
//初始化标准数组
int [] myArrayOfInts = {0,1,2,3,4,5,6,7,8,9};
//初始化整数的泛型List<>
List myGenericList = new List{0,1,2,3,4,5,6,7,8,9};
C#几乎扩展了所有接口的非泛型版本,是为了更好地实现类支持非泛型版本的旧功能。向上兼容性。
我们先来看一下,方便我们拿来直接使用的泛型方法有哪些:
List类:
List<>类是System.Collections.Generic命名空间的最常用的类型,是一个可以动态变化的列表;
static void UseGenericList()
{
//构建一个Person对象列表 Person有三个属性:Firstname、Lastname、Age
List<Person> people = new List<Person>()
{
new Person {Firstname="Homer",Lastname="Simple",Age=34},
new Person {Firstname="Flame",Lastname="Haro",Age=23},
new Person {Firstname="James",Lastname="Leons",Age=45}
};
Console.WriteLine("Items in List:{0}",people.Count);
// 枚举列表
foreach(Person p in people)
Console.Writeline(p);
//insert into a new obj.people
people.insert(2,new Person{Firstname="Phines",Lastname="Leons",Age=23});
Console.WriteLine("Items is list:{0}",people.Count);
//将数据复制到新数组中
Person[] arrayofPerson = people.toArray();
for (int i;i<arrayofPerson.length;i++)
{
Console.WriteLine("First Names:{0}",arrayofPerson[i].Firstname);
}
}
其中,toArray、Count等函数都是List定义的内部成员方法,更多详情请参考.NET FrameWork文档。
Stack类,表示以后进先出的方式维护数据的集合。包含Push和Pop方法,跟数据结构中的栈的数据存储理念类似;
以下代码创建一个Person对象的栈:
static void UseGenericStack()
{
//构建一个Person对象列表 Person有三个属性:Firstname、Lastname、Age
Stack<Person> Stackofpeople = new Stack<Person>();
Stackofpeople.push(new Person {Firstname="Homer",Lastname="Simple",Age=34});
Stackofpeople.push(new Person{Firstname="Flame",Lastname="Haro",Age=23});
Stackofpeople.push(new Person {Firstname="James",Lastname="Leons",Age=45});
//观察栈顶的项,取出,再次观察现在栈顶的元素重复操作
Console.WriteLine("The First Person is {0}:",Stackofpeople.peek());
Console.WriteLine("The First Person is {0}:",Stackofpeople.pop());
...
//依次弹出栈顶元素
//在最后检测栈是否是空
try
{
Console.WriteLine("The First Person is {0}:",Stackofpeople.peek());
Console.WriteLine("The First Person is {0}:",Stackofpeople.pop());
}
catch(InvalidOperationException ex)//InvalidOperationException是当栈为空时抛出的异常
{
Console.WriteLine(ex.Message);
}
}
Queue<T>类,对应处理数据结构的队列。处理逻辑就像我们排队买票一样,先到先得。
Queue<T>类型的成员
Dequeue() 移除并返回Queue<T>开始处的对象
Enqueue() 在Queue<T>的末尾处添加一个对象
Peek() 返回Queue<T>开始处的对象,但不移除
用Person类构建一个Queu<T>,来模仿一群人开始买饭的现象;
// this is a Sup funcation
static void GetFood(Person p)
{
Console.WriteLine("{0} person coming get your Food :",p.Firstname);
}
//
static void UseGenericQueue()
{
//构建一个Person对象列表 Person有三个属性:Firstname、Lastname、Age
Queue<Person> peopleQ = new Queue<Person>();
peopleQ.Enqueue(new Person {Firstname="Homer",Lastname="Simple",Age=34});
peopleQ.Enqueue(new Person{Firstname="Flame",Lastname="Haro",Age=23});
peopleQ.Enqueue(new Person {Firstname="James",Lastname="Leons",Age=45});
//watch the firstperson in line
Console.WriteLine("{0} is first in line!:",peopleQ.Peek().Firstname);
GetFood(peopleQ.Dequeue());
GetFood(peopleQ.Dequeue());
GetFood(peopleQ.Dequeue());
try
{
GetFood(peopleQ.Dequeue());
}
catch(InvalidOperationException e)
{
Console.WriteLine("Error {0}",e.Message);
}
}
创建自定义泛型方法
我们常会用到交换操作,如果我们需要操作一个证书的交换方法,我们可以这样写:
//交换两个整数
static void Swap(ref int a ,ref int b)
{
int temp;
temp= a ;
a= b;
b= temp;
}
如果就是这样还好,如果我们希望交换的是一系列不同类型的值,交换一条记录中的每一个字段值,有各种类型,那我们就要写多个类型的交换方法,这会给后期的维护带来很大的开销
我们也可以创建一个非泛型的方法来操作object对象,同样我们要面临拆箱、装箱、类型安全、显示转换等问题,当方法逻辑一样,知识参数不同,那么就可以使用泛型:
static void Swap<T>(ref T a ,ref T b)
{
Console.WriteLine("this is Swap funcation,T type is :",typeof(T));
T temp;
temp = a;
a = b;
b = temp;
}
这样就可以随心所欲的进行各种不同类型的交换。