面向对象的高级编程1.ppt_第1页
面向对象的高级编程1.ppt_第2页
面向对象的高级编程1.ppt_第3页
面向对象的高级编程1.ppt_第4页
面向对象的高级编程1.ppt_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第4章 面向对象的高级编程,封装、继承与多态 版本控制 接口 委托 序列化和反序列化 泛型处理,4.1 封装、继承与多态,封装:隐藏调用者不需要了解的信息。 继承:简化类的设计。 多态:类为名称相同的方法提供不同实现方式的能力。,4.1.1 封装,模块信息通过类封装 类的成员通过字段、属性、方法、事件封装 私有的用private 可以继承的用protected 公共成员用public 不要为了方便把什么都定义成public 不要为了方便把什么都定义成static,/MyClass.cs using System; namespace ConsoleTest class MyClass private int number; public int Number get return number; set if (value 0) number = value; ,/Program.cs using System; namespace ConsoleTest public class Program public static void Main() MyClass me = new MyClass(); me.Number = -1; Console.WriteLine( “Number:0“, me.Number); Console.ReadLine(); 回答:既然没有赋值,为什么输出0?,4.1.2 继承,两种实现继承的方式:类继承和接口继承。 类继承只允许单一继承 ,接口可实现多重继承。 被继承的类叫基类 继承自其他类的类叫扩充类,继承(续),声明扩充类的语法: 访问修饰符 class 扩充类名称:基类名称 /程序代码 扩充类继承了所有定义在基类中数据的定义和 方法。但是不继承构造函数,而且扩充类不继承基类的数据值。,using System; Namespace InheritanceExample public class Animal public Animal() Console.WriteLine(“Hello Animal!”); public void Eat() Console.WriteLine(“Eating”); public class Cat:Animal public Cat() Console.WriteLine(“Hello Cat!”); ,Class program static void Main(string args) Cat e=new Cat(); e.Eat(); Console.ReadLine(); ,/MyNewClass.cs using System; namespace ConsoleTest class MyNewClass : MyClass private string str = “hello“; public void Hello() Number = 2; Console.WriteLine(str); ,密封类 不能被其他类继承的类。 用sealed 关键词修饰。(错误的例子) public class Hello public sealed void SayHello() Console.WriteLine(“this is base class”); Public class NewHello:Hello public new void SayHello() Console.WriteLine(“this is extend class”); class program static void main(string args) NewHello me =new NewHello(); me.SayHello(); Console.ReadLine(); ,4.1.3 多态性,多态性是指类为名称相同的方法提供不同实现方式的能力。利用多态性,就可以调用类中的某个方法而无需考虑该方法是如何实现的。 有以下几种实现多态性的方式。 第一种方式是通过继承实现多态性。 第二种方式是通过抽象类实现多态性。 第三种方式是通过接口实现多态性。,1. 虚拟方法 在基类中,如果想让某个方法或者事件被扩充类重写,可以使用修饰符virtual表明: public virtual void myMethod() /程序代码 扩充类则用override重写: public override void myMethod() /程序代码 ,使用虚拟方法与重写方法时,需要注意下面几个方面: 1) 虚拟方法不能声明为静态(static)的。因为静态的方法是应用在类这一层次的,而多态性只能在对象上运作。 2) virtual不能和private一起使用。因为声明为private就无法在扩充类中重写了。 3) 重写方法的名称、参数个数、类型以及返回值都必须和虚拟方法的一致。,/MyClass.cs using System; namespace ConsoleTest class MyClass public virtual void Hello() Console.WriteLine(“123“); ,/MyNewClass.cs using System; namespace ConsoleTest class MyNewClass : MyClass private string str = “hello“; public override void Hello() Number = 2; Console.WriteLine(str); ,只有使用override修饰符,才能重写基类的方法;否则,在继承的类中声明一个与基类方法同名的方法会隐藏基类的方法。,引起混乱的方法声明 class Shape public virtual void F() class Triangle:Shape public virtual void() class program static void Main(string args) Triangle me=new Triangle(); me.F(); Console.ReadLine(); ,2. 隐藏基类的方法 在扩充类中,可以使用new关键字来隐藏基类的方法,即使用一个完全不同的方法取代旧的方法。 与方法重写不同的是,使用new关键字时并不要求基类中的方法声明为virtual,只要在扩充类的方法前声明为new,就可以隐藏基类的方法。,class Hello public void SayHello() Console.WriteLine(“base class“); ,class NewHello :Hello public new void SayHello() Console.WriteLine(“extend class“); Class program static void Main(string args) Hello b=new Hello(); b.SayHello(); NewHello d=new NewHello(); d.SayHello(); Console.ReadLine(); ,3. 抽象类 抽象类使用abstract修饰符,用于表示所修饰的类是不完整的,即类中的成员(例如方法)不一定全部实现,可以只有声明没有实现。抽象类只能用作基类。抽象类与非抽象类相比有以下主要不同之处: 第一是抽象类不能直接被实例化,只能在扩充类中通过继承使用,对抽象类使用new运算符会产生编译时错误 第二个不同点是抽象类可以包含抽象成员,而非抽象类不能包含抽象成员。当从抽象类派生非抽象类时,这些非抽象类必须具体实现所继承的所有抽象成员。,abstract class A public abstract void F(); abstract class B public void G(); class C:B public override void F() ,B没有实现方法f(),必须定义为抽象 C重写了抽象方法f(),可以定义为非抽象类,public abstract class Shape public virtual void Draw() Console.WriteLine(“画图”); public abstract void Rotate(); public class Square:Shape public override void Draw() Console.WriteLine(“画一个正方形!”); public override void Rotate() Console.WriteLine(“顺时针方向旋转正方形!”); public class Program public static Main(string args) Square me = new Square(); me.Draw(); me.Rotate(); Console.ReadLine(); ,抽象类和抽象方法,课堂练习,编写一个控制台应用程序,完成下列功能,并回答提出的问题。 1) 创建一个类A,在构造函数中输出“A”,再创建一个类B,在构造函数中输出“B”。 2) 从A继承一个名为C的新类,并在C内创建一个成员B。不要为C创建构造函数。 3) 在Main方法中创建类C的一个对象,写出运行程序后输出的结果。 4) 如果在C中也创建一个构造函数输出“C”,整个程序运行的结果又是什么?,using System; public class A public A() Console.WriteLine(“A“); public class B public B() Console.WriteLine(“B“); ,public class C : A B newb = new B(); class MainClass public static void Main() C newc = new C(); Console.ReadLine(); ,输出结果: B A 如果在C中也创建一个构造函数输出“C”,即添加: public C() Console.WriteLine(“C“); 则整个程序运行的结果为: B A C,4.2 版本控制,用C#编写方法时,如果在扩充类中重写基类的方法,需要用override声明;要隐藏基类的方法,需要用new声明,这就是C#进行版本控制的依据。 在C#中,所有的方法默认都是非虚拟的,调用非虚拟方法时不会受到版本的影响,不管是调用基类的方法还是调用扩充类的方法,都会和设计者预期的结果一样执行实现的程序代码。 相比之下,虚拟方法的实现部分可能会因扩充类的重写而影响执行结果。也就是说,在执行时期调用虚拟方法时,它会自动判断应该调用哪个方法。,版本控制使用new class A public void Method() Console.WriteLine(“A.Method”); class B:A public new void Method() Console.WriteLine(“B.Method”); class program Static void Main(string args) A a=new A(); B b=new B(); A c=b; a.Method(); b.Method(); c.Method(); ,输出结果: A.Method B.Method A.Method,版本控制使用virtual与new class A public virtual void Method() Console.WriteLine(“A.Method”); class B:A public new virtual void Method() Console.WriteLine(“B.Method”); class program Static void Main(string args) A a=new A(); B b=new B(); A c=b; a.Method(); b.Method(); c.Method(); ,输出结果: A.Method B.Method A.Method,版本控制使用virtual,new,与override class A public virtual void Method() Console.WriteLine(“A.Method”); class B:A public new virtual void Method() Console.WriteLine(“B.Method”); class C:B public override void Method() Console.WriteLine(“C.Method”); class program Static void Main(string args) A a=new A(); B b=new C(); A c=b; a.Method(); b.Method(); c.Method(); ,输出结果: A.Method C.Method A.Method,4.3 接口,接口的主要特点是只有声明部分,没有实现部分。 接口成员的实现是通过类完成的。 定义在接口中的方法都是public的。 使用interface关键字声明一个接口。常用的语法是: 访问修饰符 interface 接口名称 / 接口体 public interface Itest int sum() public interface Itest public int sum(); ,一般情况下,以大写的“I”开头指定接口名,表明这是一个接口。 需要在不相关的类中实现同样的功能时,可以使用接口。 当其他设计者调用了声明的接口后,就不能随意更改接口的定义,接口与抽象类,类可以继承自多个接口 类仅能从一个抽象类或其他类型的单个类继承 如果预计要创建组件的多个版本,创建抽象类 如果创建的功能将在大范围内的完全不同的对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。 如果设计小而简练的功能块,则使用接口;如果设计 大的功能单元,则使用抽象类; 如果要在组件的所有实现间提供通用的已实现功能,使用抽象类。,接口声明与实现 using System; Namespace InterfaceExample1 interface Ifunction1 int sum(int x1,int x2); interface Ifunction2 string strget;set class MyTest:ifunction1, ifunction2 private string mystr; public MyTest() public MyTest()mystr=str; public int sum(int x1,int x2) return x1+x2; public string str get return mystr; set mystr=value; ,Class program static void Main(string args) MyTest a=new MyTest(); /直接访问实例 Console.WriteLine(a.sum(10,20); MyTest b=new MyTest(“How are you”); Console.WriteLine(b.str); /使用接口 Ifunction1 f1=(Ifunction1)a; Console.WriteLine(f1.sum(20,30); Ifunction1 f2=(Ifunction2)b; Console.WriteLine(f2.str); Console.ReadLine(); ,当需要在不相关的类中实现相同的功能,可以使用接口,显示接口实现 using System; Namespace InterfaceExample2 interface Ifunction int sum(int x1,int x2); class MyTest:Ifunction int Ifunction.sum(int x1,int x2) return x1+x2; ,Class program static void Main(string args) /MyTest a=new MyTest(); / Console.WriteLine(a.sum(10,20); MyTest myTest=new MyTest(); Ifunction1 b=(Ifunction)myTest; Console.WriteLine(b.sum(20,30); Console.ReadLine(); ,对于显示实现的方法,不能通过类的实例进行访问,必须使用接口的实例,通过接口实现多继承 using System; using System.Collections.Generic; using System.Text; Namespace InterfaceExample3 class MyBaseClass1 return x1+x2; interface IBasefunction int Multiply(int x1, int x2); class MyBaseClass2: IBasefunction public int Subtract(int x1,int x2) return x1-x2; int Ibasefunction. Multiply(int x1,int x2) return x1*x2;,Interface Ifunction1 int add(int x1,int x2); Interface Ifunction2 int Subtract(int x1,int x2); Class MyClass: MyBaseClass, Ifunction1, Ifunction2 int Ifunction1. add(int x1,int x2) MyBaseClass1 class1=new MyBaseClass1(); Return class1.add(x1,x2); int Ifunction2. Subtract(int x1,int x2) MyBaseClass2 class2=new MyBaseClass2(); Return class2. Subtract(x1,x2); public void Hello(),Class program static void Main() MyClass myclass=new MyClass(); Ifunction1 f1=(Ifunction1)myClass; Console.WriteLine(f1.add(5,2); Ifunction2 f2=(Ifunction2)myClass; Console.WriteLine(f2.Subtract(5,2); IBasefunction f3=(IBasefunction)myClass; Console.WriteLine(f3.Multiply(5,2); myclass.Hello(); ,4.4 委托,委托的特点是可以将方法作为参数传递。 就像A(方法)委托B(delegate)完成一些事(把方法当作参数传递给B),你如果想找A做什么事(想调用方法A),而A这时候正在忙(系统不允许在这种情况下调用方法A),但是你可以告诉B(可以调用委托B),其最终效果和告诉A一样(调用委托B和调用方法A效果相同)。但是区别是B可以在他认为合适的时候再告诉A(委托可以等某些条件满足时再调用方法A)。而B什么时候告诉A,你就不用操心了。,(1)声明样板 public delegate string MyDelegate(string name); (2)准备调用方法 public static string FunctionA(string name) public static string FunctionB(string name) (3)定义delegate类型的处理函数 public static void MethodA(MyDelegate Me) Console.WriteLine(Me(“张三”); (4)创建实例,传入准备调用的方法名 myDelegate a=new myDelegate(Function A);,Using System; Namespace DelegateExample public delegate string MyDelegate(string name); public class Program public static string FunctionA(string name) return “A say Hello to “+name; public static string FunctionB(string name) return “B say Hello to “+name; public static void MethodA(MyDelegate Me) Console.WriteLine(Me(“张三”); ,Public static void Main() MyDelegate a =new MyDelegate(FunctionA); MyDelegate b =new MyDelegate(FunctionB); MethodA(a); MethodA(b); ,4.5 序列化与反序列化,序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。两个过程结合可以存储和传输数据。 .NET Framework 提供两种序列化技术: 1) 二进制序列化 这种序列化可以保持类型不变,即可以在应用程序的不同调用之间保留对象的状态。 2) XML和SOAP序列化 这种序列化仅序列化公共属性和字段,不保存类型。,4.5.1 二进制序列化与反序列化 1序列化: Serializable public class AuthUserEntry private string accountName; private int accoun; public string AccountName public int AccountId . ,AuthUserEntry user=new AuthUserEntry(); user.AccountId=9912053; user.AccountName=“xxxx”; /二进制序列化 IFormatter formater=new BinaryFormatter(); Stream stream=new FileStream(“UserInfo.bin”, FileMode.Create,FileAccess.Write,FileShare.None); Formater.Serialize(stream,user); Stream.Close(); 本段代码,创建流的实例和使用的格式接口后,对此格式接口调用Serialize方法,类中所以成员变量都将被序列化。,序列化重要功能:一是将对象的状态保持在存储媒体中,以便以后可以重新创建精确的副本;而是通过值将对象从一个应用程序域发送到另一个应用程序域中,2.反序列化 将对象、返回以前的状态。首先,创建用于读取的流和格式化接口,然后用格式化接口反序列化该对象。 IFormatter formatter = new BinaryFormatter(); Stream stream = new FileStream(“UserInfo.bin”, FileMode.Open,FileAccess.Read,FileShare.Read); AuthUserEntry me=(AuthUserEntry)formatter.Deserialize(stream); stream.Close(); 注意:反序列化一个对象时不调用其构造函数,using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; namespace BinarySerializableExample Serializable public class AuthUserEntry private string accountName; private int accountId; public string AccountName get return accountName; set accountName=value; public int AccountId get return AccountId; set AccountId =value; ,Class Account static void Main(string args) AuthUserEntry user =new AuthUserEntry(); user.AccountId=9912053; user.AccountName=“xxxx”; IFormatter formater=new BinaryFormatter(); Stream stream=new FileStream(“UserInfo.bin”, FileMode.Create,FileAccess.Write,FileShare.None); Formater.Serialize(stream,user); Stream.Close(); Stream stream = new stream=new FileStream(“UserInfo.bin”, FileMode.Open,FileAccess.Read,FileShare.Read); AuthUserEntry me=(AuthUserEntry)formatter.Deserialize(stream); stream.Close(); Consile.Write(“账号”:0”,me.AccountId); Consile.Write(“账户名”:0”,me.AccountId); ,3.有选择序列化 public class MyObject NoSerializedpublic int n2; Public string str; ,XML和SOAP序列化与反序列化,对XML和SOAP流对象进行序列化和反序列化,此操作一般用于将对象转换为网络中容易传输的格式。 XML序列化仅将对象的公共字段和属性值序列化为XML流,不转换方法、索引器、私有字段或只读属性(只读集合除外)。 若要序列化对象的所有字段和属性,可使用BinaryFormatter序列化。 XML序列化不包括类型信息,即不保证序列化后的对象在被反序列化时,变为同一类型对象。,XmlSerializer类,将下列各项序列化 公共类的公共读/写属性和字段 实现ICollection或IEnumerable的类(注意:只有集合被序列化,而公共属性不会) XmlElement对象 XmlNode对象 DataSet对象,1.序列化对象 创建序列化对象并设置它的公共属性和字段 AuthUserEntry user=new AuthUserEntry(); user.AccountId=9912053; user.AccountName=“XXXX”; 使用该对象的类型构造Xmlserializer XmlSerializer mySerializer = new XmlSerializer(typeof(AuthUserEntry); /创建了StreamWriter对象完成对文件的写操作 StreamWriter myWriter=new StreamWriter(“UserInfo.xml”); 调用Serialize方法以生成对象的公共属性和字段的XML流表示形式或文件表现形式 mySerializer.Serialize(myWrite,user); myWriter.Close();,2.反序列化对象 使用反序列化对象的类型构造XmlSerializer AuthUserEntry me; XmlSerializer mySerializer=new XmlSerializer(typeof(AuthUserEntry); /创建FileStream读取文件 FileStream myFileStream= new FileStream(“UserInfo.xml”,FileMode.Open); 调用Deserialize方法以产生该对象的副本。在反序列化时,必须将返回的对象强制转换为原对象类型。 me=(AuthUserEntry) mySerializer. Deserialize(myFileStream); myFileStream.Close();,XML 序列化和反序列化,using System; using System.IO; using System.Sml.Serialization; namespace XMLSerializableExample Serializable public class AuthUserEntry private string accountName; private int accountId; public string AccountName get return accountName; set accountName=value; public int AccountId get return AccountId; set AccountId =value; ,Class Account static void Main(string args) /构造要序列化的对象 AuthUserEntry user =new AuthUserEntry(); user.AccountId=9912053; user.AccountName=“xxxx”; /构造XmlSerializer对象 XmlSerializer mySerializer = new XmlSerializer(typeof(AuthUserEntry); /创建了StreamWriter对象完成对文件的写操作 StreamWriter myWriter=new StreamWriter(“UserInfo.xml”); /调用Serialize方法实现序列化 mySerializer.Serialize(myWrite,user); myWriter.Close(); AuthUserEntry me; /用对象类型构造XmlSerializer实例 XmlSerializer mySerializer=new XmlSerializer(typeof(AuthUserEntry); /创建FileStream读取文件 FileStream myFileStream= new FileStream(“UserInfo.xml”,FileMode.Open);,/调用 Deserialize方法并强制转换返回对象类型 me=(AuthUserEntry) mySerializer. Deserialize(myFileStream); myFileStream.Close(); Consile.Write(“账号”:0”,me.AccountId); Consile.Write(“账户名”:0”,me.AccountId); ,4.6 泛型处理,泛型(Generic)用于定义未指定某些细节的类,C+中称为模板 ,主要用于解决一系列类似的问题,即通过参数化类型来实现在同一份代码上操作多种数据类型。 一般用在集合和在集合上运行的方法中。.NET框架2.0以前的版本不支持泛型。 使用泛型具有以下明显的优点: 1)可以避免以下两个问题:内部实现代码冗余和困扰开发人员的含混不清的编译器错误提示; 2) 类型安全; 3)二进制代码重用; 4)性能; 5)清晰性。,泛型的定义,访问修饰符返回类型泛型支持类型 类型名称 定义一个泛型类 class Node T data; Node next; 定义一个泛型方法 Void Swap(ref T item1 ,ref T item2) T temp=item1; item1=item2; item2=temp; ,泛型的引用,引用一个泛型类 class node8bit:Node 引用一个泛型方法 Decimal d1=0,d2=2; Swap(ref d1,ref d2) 定义一个类或者方法时,可以利用泛型代表任何一种类型,在引用的时候再具体指定类型。 由于泛型可以是任何类型,因此只定义一次方法的参数类型就可以实现所有类型的引用。,常用泛型,凡是有对应泛型类型的类就尽量不要使用早期提供的非泛型类型的类。下面的泛型类型对应于低版本提供的类型: 泛型类 低版本提供的非泛型类 List ArrayList Dictionary Hashtable Queue Queue Stack Stack SortedList SortedList,实验中用到的泛型,List :通过索引访问对象的强类型列表,提供用于对列表进行搜索,排序和操作。 常用方法: Add:将带有指定键和值的元素添加到List 中 Insert:再列表中插入一个元素 Contains:测试该列表中是否存在某元素 Remove:从Dictionary中移除带指定键元素 Clear:移除列表所有元素,实验中用到的泛型(续),Dictionary :提供从一组键到一组值的映射 常用方法: Add:将带有指定键和值的元素添加到Dictionary中 TryGetValue:获取与指定键相关联的值 ContainsKey:测试Dictionary中是否存在指定键的元素 Remove:从Dictionary中移除带指定键元素,实验中用到的泛型(续),Queue :表示对象的先进先出集合 常用方法: Enqueque:将指定元素插入队尾 Dequeue:队列首元素出列,实验中用到的泛型(续),Stack :表示同一任意类型的实例的大小可变的后进先出集合 常用方法: Push:将指定元素插入栈顶 Pop:将栈顶元素弹出,实验中用到的泛型,SortedList :按键排序的键值对集合,是运算复杂度为 O(log n) 的二进制搜索树 。 常用方法: Add方法:将带有指定键和值的元素添加到SortedList 中 例如(注意:如果插入重复的键会产生异常): SortedList openWith = new SortedList(); openWith.Add(“txt“, “notepad.exe“); openWith.Add(“bmp“, “paint.exe“);,实验中用到的泛型(续),TryGetValue方法:获取与指定的键相关联的值 。 例如: string value = “; if (openWith.TryGetValue(“tif“, out value) Console.WriteLine(“For key = “tif“, value = 0.“, value); else Console.WriteLine(“Key = “tif“ is not found.“); ,实验中用到的泛型(续),ContainsKey 方法:确定 SortedList中 是否包含指定的键。 例如: if (!openWith.ContainsKey(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论