各位同学:
大家好!
这次上机的主要任务是研究泛型、类的派生与继承、多态等内容。
1. 研究cs095,完成如下工作:
a.了解:如果两个重载的函数只有参数的类型不同,而其他的都相同,这样的两个函数就应该用泛型函数统一起来。
b.了解:任何一种数据类型E的数组E[],都一定实现了IEnumerable
接口。只有实现了这个接口的容器,才能使用foreach语句进行遍历。
c.了解:在C#中,能够用throw抛出的对象一定是Exception(或其派生类)的对象。用户自定义异常类通常继承自ApplicationException类。其中,成员属性StackTrace描述了异常对象从抛出到被捕获之前的所有函数调用及其位置。
using System; class FullStackException : ApplicationException { // parameterless constructor public FullStackException() : base( "Stack is full" ) { // empty constructor } // end FullStackException constructor // one-parameter constructor public FullStackException( string exception ) : base( exception ) { // empty constructor } // end FullStackException constructor } // end class FullStackException
d.泛型的类型参数通常是需要约束的。可以同时指定多个约束,用逗号格开,其中new()约束必须放在最后。请自己编写一个带多个约束的泛型方法。
约束类型:
约束 | 说明 |
where T:struct | 使用结构约束,类型T必须是值类型 |
where T:class | 类约束指定,类型T必须是引用类型 |
where T:IFoo | 指定类型T必须执行接口IFoo |
where T:Foo | 指定类型T必须派生于基类Foo |
where T:new() | 这是一个构造函数约束,指定类型T必须有一个默认构造函数 |
where T:U | 这个约束也可以指定,类型T1派生于泛型类型T2。该约束也称为裸类型约束 |
class Stack< E > where E:class,new()
2. 研究samename.cs,了解如何在派生类中访问被隐藏的基类成员(使用关键字base),了解如果有意隐藏基类成员,最好使用关键字new。同时,思考如下问题:为什么最好完全避免隐藏的发生。
using System; using System.Collections.Generic; using System.Text; namespace samename { class A { protected int x; public A(int i) { x = i; } public void show() { Console.WriteLine(x); } } class B : A { double x; public B(double d) : base(1) { x = d; } public void show() { Console.WriteLine(x); } public void show1() { Console.WriteLine(base.x); } } class Program { static void Main(string[] args) { B b = new B(5.8); b.show(); b.show1(); Console.ReadLine(); } } }
Warning 1 'samename.B.show()' hides inherited member 'samename.A.show()'. Use the new keyword if hiding was intended. C:/Documents and Settings/Administrator/My Documents/Visual Studio 2008/Projects/ConsoleApplication1/ConsoleApplication1/Program.cs 27 21 ConsoleApplication1
使用new关键字可以消除警告,但效果不变:
public new void show();
声明的可访问性
含义
public
访问不受限制。
protected
访问仅限于包含类或从包含类派生的类型。
internal
访问仅限于当前程序集。
protected internal
访问仅限于从包含类派生的当前程序集或类型。
private
访问仅限于包含类型。
3. 研究Convert.cs,思考如下问题:
a.我们说把A类转换成B类,是通过A类对象创建一个新的B类对象吗?如果不是,那么转换的具体含义又是什么呢?
b.既然向下转换(基类转换成派生类)是不安全的,那么为什么不在编译的时候就禁止?
using System; using System.Collections.Generic; using System.Text; namespace cs043 { class A { } class B : A { }; class Program { static void Main(string[] args) { A a = new A(); // A a = new B(); B b = (B)a; Console.ReadLine(); } } }
使用A a = new A(); 则运行时报错;
未处理的异常: System.InvalidCastException: 无法将类型为“cs043.A”的对象强制转
换为类型“cs043.B”。
在 cs043.Program.Main(String[] args) 位置 C:/Documents and Settings/Administr
ator/My Documents/Visual Studio 2008/Projects/ConsoleApplication1/ConsoleApplica
tion1/Program.cs:行号 15
5.5 C#高级编程:用户定义的数据类型转换(1)
5.5 C#高级编程:用户定义的数据类型转换(2)
在进行数据类型转换时,会检查被引用的对象。因为基类引用实际上可以引用一个派生类实例,所以这个对象可能是要转换的派生类的一个实例。如果是这样,转换就会成功,派生的引用被设置为引用这个对象。但如果该对象不是派生类(或者派生于这个类的其他类)的一个实例,转换就会失败,抛出一个异常。
使用A a = new B(); 正确
4. 研究KnowType.cs,了解如下事实:
a.通过is操作可以判断一个引用的动态数据类型,这种判断不是排他性的。派生类对象可以被当作任何一个基类对象看待。
b.obj.GetType()返回一个System.Type类型的对象。通过MSDN查看一下该类对象有哪些成员函数。
using System; using System.Collections.Generic; using System.Text; namespace knowtype { class Base { } class Derived : Base { } class Program { static int TypeID(object ob) { if (ob is Base) return 1;if (ob is Derived) return 2; //注意:把这句话移到第二行,运行结果就会有问题,试试看! return 0; } static void Distinguish(object obj) { switch (TypeID(obj)) { case 1: Console.WriteLine("Base"); break; case 2: Console.WriteLine("Derived"); break; default: Console.WriteLine(obj.GetType()); break; } } static void Main(string[] args) { Base a = new Base(); Derived b = new Derived(); Distinguish(a); Distinguish(b); Distinguish(1); Console.ReadLine(); } } }
5. 研究cs102,完成如下工作:
a.了解:可用typeof操作符直接作用于某种数据类型,从而返回相应的Type对象。
b.定义byte a,b;利用(a+b).getType()查看该表达式的数据类型。
(a+b)类型是System.Int32
6. 研究virtual.cs,完成如下工作:
a.了解:抽象类中可以不包含任何一个抽象方法。但包含抽象方法的类一定是抽象类。抽象方法一定是虚方法。
b.了解:虚方法在派生类中如果不被显式改写,那么将继承基类中的版本。
c.了解:如果用new关键字在派生类中定义了一个与基类的虚方法同名的方法,实际上是阻止了该虚方法的进一步改写。
using System; using System.Collections.Generic; using System.Text; namespace virtualfunc { abstract class Concrete { public void show() { } } abstract class A { public abstract void show(); } class B : A { public override void show() { Console.WriteLine("B"); } } class C : B { override public void show() //将override关键字改为new再试一试 { Console.WriteLine("C"); } } class Program { static void Main(string[] args) { // Concrete obj = new Concrete(); //只要是用abstract说明的类一定是抽象类,不能被实例化 A a = new B(); B b=(B)a; a.show(); b.show(); a = new C(); a.show(); ((C)a).show(); Console.ReadLine(); } } }
7. 研究AbstractShapes。完成如下的工作:
a.了解:属性(Property)表面上看起来像字段,但实际上是类的方法成员,所以可以声明抽象属性,并在派生类中改写。
b.了解:基类中的虚方法,在派生类的成员函数中可以被“实调用”,即用base.f()的形式调用。函数的入口地址是在编译时决定的。
c.了解:用一个基类的数组,数组的每个分量指向不同的派生类对象,然后统一调用数组每个分量的虚函数,是利用多态机制进行程序设计的典型案例。