加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

泛型学习笔记(一)理解泛型提出的原因、使用几种泛型集合

(2012-01-12 21:10:55)
标签:

泛型

1.       为什么提出泛型,先来看使用非泛型集合的两个问题:性能问题类型安全问题:

1)性能问题:

装箱:显式将值类型分配给System.Object变量的过程。当我们对一个值进行装箱时,CLR就会在堆上分配新的对象并且将值类型的值复制到那个实例上,因此,返回给我们的就是新分配在堆上的对象的引用。

拆箱:把保存在对象引用中的值转换回栈上的相应值类型。CLR会验证收到的值类型是不是等价于装箱的类型。例:

int myInt = 25;

            object boxedInt = myInt; //int装箱为object

            int unboxedInt = (int)boxedInt;//将引用拆箱为相应的int

                      try

            {

                string unboxedInt2 = (string)boxedInt; //此行发生错误,不等价于装箱的类型

            }

            catch (InvalidCastException ex)//拆箱为错误的数据类型将触发运行时异常

            {

                Console.WriteLine(ex.Message);

            }

         ArrayList来说,它操作的原型均为System.Object数据,传入数据时会进行自动装箱,使用类型索引器从ArrayList中获取项时又会自动拆箱,这样会引起性能问题。例:

//在传递给需要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);

  2)类型安全问题:

         在泛型之前,要解决类型安全问题的唯一方法是手工创建自定义(强类型的)集合类。

         首先,创建一个类Person

public int Age { get; set; }

            public string FirstName { get; set; }

            public string LastName{get;set;}

            public Person(){}

            public Person(string firstName, string lastName, int age)

            {

                Age = age;

                FirstName = firstName;

                LastName = lastName;

            }

            public override string ToString()

            {

                return string.Format("Name:{0}{1},Age:{2}",FirstName,LastName,Age);

            }

         其次,自定义集合类PersonCollection

                private ArrayList arPeople = new ArrayList();

           //为提供者进行转换

           public Person GetPerson(int pos)

           {

               return (Person)arPeople[pos];

           }

           //只插入Person对象

           public void AddPerson(Person p)

           {

               arPeople.Add(p);

           }

           public void ClearPeople()

           {

               arPeople.Clear();

           }

           public int Count

           {

               get { return arPeople.Count; }

           }

           //支持foreach枚举

           IEnumerator IEnumerable.GetEnumerator()

           {

               return arPeople.GetEnumerator();

           }

         最后,定义这些类型后,就不用担心类型安全了,因为C#编译器会检查任何尝试插入不兼容数据类型的请求:

PersonCollection myPeolple = new PersonCollection();

            myPeolple.AddPerson(new Person("Homer","Simpson",40));

            myPeolple.AddPerson(new Person("Marge", "Simpson", 38));

            myPeolple.AddPerson(new Person("Lisa", "Simpson", 9));

            myPeolple.AddPerson(new Person("Bart", "Simpson", 7));

            myPeolple.AddPerson(new Person("Maggie", "Simpson", 2));

            //这会产生编译时错误

            //myPeople.AddPerson(new Car());

            foreach (Person p in myPeolple)

            {

                Console.WriteLine(p);

            }

结论:虽然自定义的集合可以确保类型安全,但是如果要是用这个方法,就必须为每一个希望包含的类型创建一个基本一模一样的自定义集合,比如CarCollectionIntCollection等,而且这些自定义集合并没有消除装箱/拆箱的损失,不管选择哪个类型来保存,都不能避免使用非泛型容器带来的装箱问题,所以提出了泛型。

2.       我们可以用泛型List<>来解决上面3的例子:

//List<>只能容纳Person对象

            List<Person> morePeople = new List<Person>();

            morePeople.Add(new Person("Frank","Black",50));

            Console.WriteLine(morePeople[0]);

            //List<>只能容纳整数

            List<int> moreInts = new List<int>();

            moreInts.Add(10);

            moreInts.Add(2);

            int sum = moreInts[0] + moreInts[1];

            //编译错误!不能将Person对象添加到整型列表中

            //moreInts.Add(new Person());

3.       任何使用.NET 2.0或更高版本创建的项目都应该放弃使用System.Collections中的类,而使用System.Collections.Generic中的类。

4.       <T>正式名称为类型参数,也可通俗的称为占位符,符号读作of T

5.       在基础类库中引入一个以集合为中心的新命名空间:System.Collections.Generic命名空间,该命名空间位于mscorlib.dllSystem.dll程序集内。

6.       只有类、结构、接口和委托可以使用泛型,枚举类型不可以。

7.       对比非泛型接口的实现与泛型接口的实现:

非泛型接口IComparable的实现:

public class Car : IComparable

    {

        int carID;

        public int CarID

        {

            get { return carID; }

            set { carID = value; }

        }

        //IComparable的实现

        int IComparable.CompareTo(Object obj)

        {

            Car temp = obj as Car;

            if (temp != null)

            {

                if (this.CarID > temp.CarID)

                    return 1;

                if (this.CarID < temp.CarID)

                    return -1;

                else

                    return 0;

            }

            else

            {

                throw new ArgumentException("Parameter is not a Car!");

            }

        }

    }

泛型接口IComparable<>的实现:

public class Car : IComparable<Car>

    {

        int carID;

        public int CarID

        {

            get { return carID; }

            set { carID = value; }

        }

        //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;

        }

    }

结论:对比后发现泛型接口的实现不用判断传入的参数是否为Car,因为它只能为Car,如果传入的数据类型不兼容,将得到编译时错误。

8.       使用List<T>类:

private static void UseGenericList()

        {

            // 使用集合/对象初始化语法,构建一个Person对象的列表

            List<Person> people = new List<Person>()

            {

                new Person {FirstName= "Homer", LastName="Simpson", Age=47},

                new Person {FirstName= "Marge", LastName="Simpson", Age=45},

                new Person {FirstName= "Lisa", LastName="Simpson", Age=9},

                new Person {FirstName= "Bart", LastName="Simpson", Age=8}

            };

            //打印列表中项的个数

            Console.WriteLine("Items in list: {0}", people.Count);

            // 枚举列表

            foreach (Person p in people)

                Console.WriteLine(p);

            // 插入一个新Person

            Console.WriteLine("\n->Inserting new person.");

            people.Insert(2, new Person { FirstName = "Maggie", LastName = "Simpson", Age = 2 });

            Console.WriteLine("Items in list: {0}", people.Count);

            // 将数据复制到新的数组中

            Person[] arrayOfPeople = people.ToArray();

            for (int i = 0; i < arrayOfPeople.Length; i++)

            {

                Console.WriteLine("First Names: {0}",

                  arrayOfPeople[i].FirstName);

            }

        }

注意:Insert()方法和ToArray()方法的使用

9.       使用Stack<T>

static void UseGenericStack()

        {

            Stack<Person> stackOfPeople = new Stack<Person>();

            stackOfPeople.Push(new Person { FirstName = "Homer", LastName = "Simpson", Age = 47 });

            stackOfPeople.Push(new Person { FirstName = "Marge", LastName = "Simpson", Age = 45 });

            stackOfPeople.Push(new Person { FirstName = "Lisa", LastName = "Simpson", Age = 9 });

            // 观察栈顶的项,取出,再次观察

            Console.WriteLine("First person is: {0}", stackOfPeople.Peek());

            Console.WriteLine("Popped off {0}", stackOfPeople.Pop());

            Console.WriteLine("\nFirst person is: {0}", stackOfPeople.Peek());

            Console.WriteLine("Popped off {0}", stackOfPeople.Pop());

            Console.WriteLine("\nFirst person item is: {0}", stackOfPeople.Peek());

            Console.WriteLine("Popped off {0}", stackOfPeople.Pop());

            try

            {

                Console.WriteLine("\nnFirst person is: {0}", stackOfPeople.Peek());

                Console.WriteLine("Popped off {0}", stackOfPeople.Pop());

            }

            catch (InvalidOperationException ex)

            {

                Console.WriteLine("\nError! {0}", ex.Message);

            }

        }

注意:在观察栈时,得到的永远是栈顶对象。如果栈为空,这时再调用Peek()Pop()将触发系统异常。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有