C#程序设计 c# programming 泛型 C#程序设计课程组.

Slides:



Advertisements
Similar presentations
第3-2章 类与 对象 Java类的特性 教学内容: 类的私有成员与公共成员 方法的重载 构造方法 实例成员与静态成员 重点: 重载 难点:
Advertisements

Java语言的特点 Java开发环境的搭建
第三讲 面向对象(上).
3.2 Java的类 Java 类库的概念 语言规则——程序的书写规范 Java语言 类库——已有的有特定功能的Java程序模块
项目7 面向对象高级.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
四資二甲 第三週作業 物件導向程式設計.
C#程序设计案例教程 第3章 程 序 结 构.
面向对象的程序设计(一).
C#程序设计 10软件1、2班 王槐彬 计算机工程学院.
第二章 JAVA语言基础.
第7单元 面向过程编程—— 继承与多态.
第八章 类和对象.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
C# 程式設計 第一部分 第1-4章 C# 程式設計 - 南華大學資管系.
第5章 异常处理 王德俊 上海交通大学继续教育学院.
第5章 面向对象程序设计 本章要点 5.1 面向对象程序设计概述 5.2 Java语言的面向对象程序设计 5.3 方法的使用和对象数组
第八章 C#高级编程.
.NET 程式設計入門(使用 C#) 講師:鄧智鴻.
第二章 C# 基础知识.
第四章 在 C# 中实现 OOP 概念.
第8章 列舉器與集合 注意: 本投影片僅供本書上課教師使用,非經同意請勿上網轉載或供拷貝.
DEV 331 深度探索 Microsoft Visual C# 2.0
程式敘述執行順序的轉移 控制與重複、方法 Lecturer:曾學文.
C#程序设计 c# programming 多线程 C#程序设计课程组.
第16章 VB.NET物件導向與.NET Framework
Ch13 集合與泛型 物件導向程式設計(2).
西南科技大学网络教育系列课程 高级语程序设计(Java) 第五章 继承、接口与范型.
第三章 C#面向对象初级编程 面向对象得程序设计越来越受到编程人员的喜爱。类和对象是面向对象程序设计中的重要概念。封装性、继承性和多态性是面向对象的特点,本章旨在全面说明C#编写面向对象程序设计的方法。
程式設計實作.
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
C#程序设计基础 $3 成员、变量和常量.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第9章 多线程 王德俊 上海交通大学继续教育学院.
第4章 数组和集合 4.1 一维数组 4.2 二维数组 4.3 Array类 4.4 交错数组 4.5 ArrayList类
第三章 流程控制與例外處理 資訊教育研究室 製作 注意:本投影片僅供上課使用,非經同意,請勿散播或轉載。
C#面向对象程序设计 $7 继承和多态性.
第6章 继承和接口设计 6.1 继 承 6.2 多态性 6.3 抽象类 6.4 接口 6.5 接口在集合排序中的应用.
視窗程式設計 (Windows Programming)
例外處理與 物件序列化(Exception Handling and Serialization of Objects)
檔案讀寫與例外處理 (File IO and Exception Handling)
第9讲 Java的继承与多态(一) 类的继承 子类的创建 方法覆盖.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
C/C++/Java 哪些值不是头等程序对象
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
$10 可空类型.
C#程序设计基础 第二章 数据类型.
第六章 属性、索引器、委托和事件.
類別與物件 I (Classes and Objects I)
第三章 C# 基础知识.
辅导课程八.
JAVA 编 程 技 术 主编 贾振华 2010年1月.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
$9 泛型基础.
第五章 介面/集合/泛型 注意: 本投影片僅供本書上課教師使用,非經同意請勿上網轉載或供拷貝.
C#程序设计基础 $3 成员、变量和常量.
Java程式初體驗大綱 大綱 在學程式之前及本書常用名詞解釋 Hello Java!程式 在Dos下編譯、執行程式
第四章 类 4.1 基础知识 4.2 构造函数 4.3 方法 4.4 属性与索引 4.5 String类
第三章 数据抽象.
C++程序设计 吉林大学计算机科学与技术(软件)学院.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
辅导课程十二.
JAVA 程式設計與資料結構 第三章 物件的設計.
判斷(選擇性敘述) if if else else if 條件運算子.
第二章 Java基本语法 讲师:复凡.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Presentation transcript:

C#程序设计 c# programming 泛型 C#程序设计课程组

教学内容 什么是泛型 泛型的类型 泛型的约束 泛型集合类型

什么是泛型?

public class Stack { private int[] m_item; public int Pop(){...} public void Push(int item){...} public Stack(int i) this.m_item = new int[i]; }

当我们需要一个栈来保存string类型时,该怎么办呢? public class Stack { private string[] m_item; public string Pop(){...} public void Push(string item){...} public Stack(int i) this.m_item = new string[i]; }

优秀的程序员会想到用一个通用的数据类型object来实现这个栈 若以后再需要long、byte类型的栈该怎样做呢?还要再复制吗? 优秀的程序员会想到用一个通用的数据类型object来实现这个栈

这个栈完美吗? public class Stack { private object[] m_item;         public object Pop(){...}         public void Push(object item){...}         public Stack(int i)         {             this.m_item = new[i];         }          } 非常灵活,可以接收任何数据类型

当Stack处理值类型时,会出现装箱、折箱操作,这将在托管堆上分配和回收大量的变量,若数据量大,则性能损失非常严重. 装箱:是指将一个值类型隐式或显式地转换成一个object类型 拆箱:是指将一个对象类型显式地转换成一个值类型 在数据类型的强制转换上还有更严重的问题(假设stack是Stack的一个实例): Node1 x = new Node1(); stack.Push(x); Node2 y = (Node2)stack.Pop(); 上面的代码在编译时是完全没问题的,但由于Push了一个Node1类型的数据,但在Pop时却要求转换为Node2类型,这将出现程序运行时的类型转换异常,但却逃离了编译器的检查

引入泛型,可以优雅地解决这些问题。 泛型用一个通过的数据类型T来代替object,在类实例化时指定T的类型,运行时(Runtime)自动编译为本地代码,运行效率和代码质量都有很大提高,并且保证数据类型安全。

引入了通用数据类型T就可以适用于任何数据类型 public class Stack<T> { private T[] m_item; public T Pop(){...} public void Push(T item){...} public Stack(int i) this.m_item = new T[i]; } 引入了通用数据类型T就可以适用于任何数据类型

//实例化只能保存int类型的类 Stack<int> a = new Stack<int>(100); a.Push(10); a.Push("8888"); //这一行编译不通过,因为类a只接收int类型的数据 int x = a.Pop(); //实例化只能保存string类型的类 Stack<string> b = new Stack<string>(100); b.Push(10); //这一行编译不通过,因为类b只接收string类型的数据 b.Push("8888"); string y = b.Pop();

泛型是C# 2.0的一个新增加的特性,它把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候进行。 泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个类型的占位符

泛型的类型

泛型:类 public class GenericList<T> { void Add(T input) { } } class TestGenericList private class ExampleClass { } static void Main() GenericList<int> list1 = new GenericList<int>(); GenericList<string> list2 = new GenericList<string>(); GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();

泛型接口 interface IPerson<T> { void PrintYourName( T t); } interface Iinterface<T> { } interface IPerson<T> { void PrintYourName( T t); } class Person { public string Name = "aladdin"; } class PersonManager : IPerson<Person> { public void PrintYourName( Person t ) Console.WriteLine( "My Name Is {0}!" , t.Name ); }

泛型方法 static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; } public static void TestSwap() { int a = 1; int b = 2; Swap<int>(ref a, ref b); System.Console.WriteLine(a + " " + b); }

泛型的约束

public class Node<T, V> where T : Stack, IComparable where V: Stack {...} 以上的泛型类的约束表明,T必须是从Stack和IComparable继承,V必须是Stack或从Stack继承,否则将无法通过编译器的类型检查,编译失败。

在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。 如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。

六种类型的约束 约束 说明 T:结构 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。有关更多信息,请参见使用可空类型(C# 编程指南)。 T:类 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。 T:new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。 T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。

接口约束 例如,可以声明一个泛型类 MyGenericClass,这样,类型参数 T 就可以实现 IComparable<T> 接口: Interface MyGenericClass<T> where T:IComparable { }

基类约束 指出某个类型必须将指定的类作为基类(或者就是该类本身),才能用作该泛型类型的类型参数。 这样的约束一经使用,就必须出现在该类型参数的所有其他约束之前。 class MyClassy<T, U> where T : class where U : struct { }

构造函数约束 可以使用 new 运算符创建类型参数的实例;但类型参数为此必须受构造函数约束 new() 的约束。 new() 约束可以让编译器知道:提供的任何类型参数都必须具有可访问的无参数(或默认)构造函数。例如: public class MyGenericClass <T> where T: IComparable, new() { // The following line is not possible without new() constraint: T item = new T(); }

泛型集合类型

使用命名空间:System.Collections.Generic 泛型最常见的用途是创建集合类 使用命名空间:System.Collections.Generic 非泛型集合类 泛型集合类 ArrayList List HashTable Dictionary Queue Stack SortedList

何时使用泛型集合 通常情况下,建议使用泛型集合,因为这样可以获得类型安全的直接优点而不需要从基集合类型派生并实现类型特定的成员。 此外,如果集合元素为值类型,泛型集合类型的性能通常优于对应的非泛型集合类型,因为使用泛型时不必对元素进行装箱。

List<T> 引入命名空间:System.Collections.Generic List<Student> students = new List<Student>(); students.Add(scofield); … students.Add(jacky); 将Student对象加入班级 将Teacher对象加入班级 编译出错 遍历List<Student>集合 只能保存Student对象 foreach (Student stu in students) { Console.WriteLine(stu.Name); } 不需类型转换

Teacher对象 Student对象 List<Student> Student对象 泛型集合可以约束集合内的元素类型 不允许添加 允许添加 List<Student> 无需转换类型 Student对象 泛型集合可以约束集合内的元素类型

List<T>的属性与方法 属性 说明 Capacity 获取或设置集合能够保存的元素总数。 Count 获取集合中实际包含的元素数。 方法 Add 将对象添加到集合的结尾处。 AddRange 将指定集合的元素添加到List集合的末尾。 Insert 将元素插入到集合的指定索引处。 Remove 从集合中移除特定对象的第一个匹配项。 RemoveAt 移除集合中指定索引处的元素。

Person p1 = new Person("张三", 30); Person p2 = new Person("李四", 20); class Person { private string _name; //姓名 private int _age; //年龄 //创建Person对象 public Person(string Name, int Age) this._name= Name; this._age = Age; } //姓名 public string Name get { return _name; } //年龄 public int Age get { return _age; } //创建Person对象 Person p1 = new Person("张三", 30); Person p2 = new Person("李四", 20); Person p3 = new Person("王五", 50); //创建类型为Person的对象集合 List<Person> persons = new List<Person>(); //将Person对象放入集合 persons.Add(p1); persons.Add(p2); persons.Add(p3); //输出第2个人的姓名 Console.Write(persons[1].Name);

练习:创建一个关于教师基本情况的记录薄,用来管理教师的基本信息,即能添加、删除和预览教师记录。 解决思路: 1.创建一个保存教师记录的泛型集合; 2.向泛型集合中添加记录; 3.从泛型集合中删除指定姓名的元素; 4.遍历泛型集合,输出所有的教师记录

using System; using System.Collections.Generic; namespace Chapter10_5 { class TeacherList List<Teacher> list = new List<Teacher>();//创建一个保存教师记录的泛型 public void AddTeacher()//向泛型集合list中添加元素 string name, sex, subject; int age; Console.WriteLine("添加教师记录,按-1退出"); Console.Write("姓名:"); name = Console.ReadLine();

while(name!="-1") { Console.Write("性别:"); sex = Console.ReadLine(); Console.Write("年龄:"); age =int.Parse(Console.ReadLine()); Console.Write("讲授学科:"); subject = Console.ReadLine(); Teacher teacher = new Teacher(name,sex,age,subject); list.Add(teacher); Console.Write("姓名:"); name = Console.ReadLine(); }

public void DelTeacher(string name)//删除泛型集合list中指定姓名的元素 { foreach(Teacher teacher in list) { if(teacher.Name==name) { list.Remove(teacher); break; } public void ViewTeacher()//预览泛型集合list中所有的教师记录 { Console.WriteLine (teacher.Name,teacher.Sex,teacher.Age,teacher.Subject);

static void Main(string[] args) { TeacherList tl = new TeacherList(); tl.AddTeacher();//添加教师记录 Console.Write("输入要删除的教师姓名:"); string name = Console.ReadLine(); tl.DelTeacher(name);//删除指定姓名name的教师记录 Console.WriteLine("操作完记录薄后教师的记录如下:"); tl.ViewTeacher();//浏览所以的教师记录 }

比如有两个数1、2,要对他们排序,首先就要比较这两个数,根据比较结果来排序。 List<T>的排序 排序基于比较,要排序,首先要比较。 比如有两个数1、2,要对他们排序,首先就要比较这两个数,根据比较结果来排序。 如果要比较的是对象,情况就要复杂一点,比如对Person对象进行比较,则既可以按姓名进行比较,也可以按年龄进行比较,这就需要确定比较规则。 一个对象可以有多个比较规则,但只能有一个默认规则,默认规则放在定义该对象的类中。 默认比较规则在CompareTo方法中定义,该方法属于IComparable<T>泛型接口

foreach (Person p in persons) { Console.WriteLine(p.Name); } class Person :IComparable<Person> { //按年龄比较 public int CompareTo(Person p) return this.Age - p.Age; } //按照默认规则对集合进行排序 persons.Sort(); //输出所有人姓名 foreach (Person p in persons) { Console.WriteLine(p.Name); }

class PersonPredicate { //找出中年人(40岁以上) List<T>的搜索 搜索就是从集合中找出满足特定条件的项,可以定义多个搜索条件,并根据需要进行调用。 class PersonPredicate { //找出中年人(40岁以上) public static bool MidAge(Person p) if (p.Age >= 40) return true; else return false; }

List<T>搜索 System.Predicate<Person> MidAgePredicate = new System.Predicate<Person>(PersonPredicate.MidAge); List<Person> MidAgePersons = persons.FindAll(MidAgePredicate); //输出所有的中年人姓名 foreach (Person p in MidAgePersons) { Console.WriteLine(p.Name); }

List<T>的扩展 如果要得到集合中所有人的姓名,中间以逗号隔开,那该怎么处理?

//定义Persons集合类 class Persons : List<Person> { //取得集合中所有人姓名 public string GetAllNames() if (this.Count == 0) return ""; string val = ""; foreach (Person p in this) val += p.Name + ","; } return val.Substring(0, val.Length - 1); //创建并填充Persons集合 Persons PersonCol = new Persons(); PersonCol.Add(p1); PersonCol.Add(p2); PersonCol.Add(p3); //输出所有人姓名 Console.Write(PersonCol.GetAllNames()); //输出“张三,李四,王五”

List<T> 与 ArrayList 通过索引删除元素 添加对象方法相同 通过索引访问集合的元素 相同点 需要装箱拆箱 无需装箱拆箱 可以增加任何类型 增加元素时类型严格检查 不同点 ArrayList List<T> 异同点

Dictionary<K,V> Dictionary<K,V>具有List<T>相同的特性 <K,V>约束集合中元素类型 编译时检查类型约束 无需装箱拆箱操作 与哈希表类似存储Key和Value的集合 利用Dictionary<K,V>存储学员集合 Dictionary<string,Student> students = new Dictionary<string,Student>(); value存储Student类型 Key存储String类型

Dictionary<K,V>属性和方法 说明 Count 获取包含在Dictionary中的键/值对的数目。 Keys 获取包含Dictionary中的键的集合。 Values 获取包含Dictionary中的值的集合。 方法 Add 将指定的键和值添加到字典中。 Remove 从Dictionary中移除所指定的键的值。

Dictionary<K,V> students.Add(scofield.Name, scofield); … student stu2 = students["周杰杰"]; students.Remove("周杰杰"); 添加一对Key/Value 通过Key获取元素 通过Key删除元素 //Dictionary<string, Student> 方式 foreach (Student student in students.Values) { Console.WriteLine(student.Name); } 遍历Values

Dictionary<K,V>与哈希表 遍历方法相同 添加对象方法相同 通过Key获取Value 相同点 需要装箱拆箱 无需装箱拆箱 可以增加任何类型 增加元素时类型严格检查 不同点 哈希表 Dictionary<K,V> 异同点

[练习]:设计一个电话号码薄,来管理联系人的电话号码,即能实现添加号码、删除和浏览号码。 解决思路: 1.创建一个用来保存电话号码/联系人姓名的字典; 2.向字典中添加号码; 3.从字典中删除指定姓名的号码; 4.浏览字典中的号码。

class TelCode { private string phonecode; private string name; public string Phonecode get { return phonecode; } set { phonecode = value; } } public string Name get { return name; } set { name = value; } } }

class TelBook { //定义一个保存电话号码/姓名对的集合 Dictionary<string, TelCode> dictionary = new Dictionary<string, TelCode>(); public void AddPhoneCode()//添加电话号码记录 { string phonecode, name; Console.WriteLine("输入电话号码和联系人姓名,按-1退出"); while(true) {Console.Write("电话号码:"); phonecode = Console.ReadLine(); if (phonecode=="-1") break; Console.Write("姓名:"); name = Console.ReadLine(); TelCode telcode=new TelCode(); telcode.Phonecode=phonecode; telcode.Name=name; dictionary.Add(phonecode, telcode); }

public bool DelPhoneCode(string phonecode)//按指定号码删除元素 { return dictionary.Remove(phonecode); } public void ViewPhoneCode()//列出集合中的姓名/电话号码 Console.WriteLine("{0,-10}{1,-10}", "姓名", "电话号码"); foreach(TelCode telcode in dictionary.Values)//遍历集合中所有的值 Console.WriteLine(telcode.Name,telcode.Phonecode);

static void Main(string[] args) { TelBook tb = new TelBook();//创建一个实例 tb.AddPhoneCode();//添加电话号码 Console.Write("输入要删除的电话号码:"); string telcode = Console.ReadLine(); tb.DelPhoneCode(telcode);//删除指定号码对应的元素 tb.ViewPhoneCode();//预览集合中的姓名/号码 }