1.`简述在C#语言中public、protected、private、internal、protected internal 5个修饰符的作用。
(5分)答案:a.private修饰private,私有修饰符,用于设置类或类成员的访问权限仅为所属类的内部。
需要访问私有类成员时,可通过get和set访问器读取或修改。
b.protected修饰符protected修饰符用于设置类或类成员的访问权限仅为所属类及子类的内部。
c.internal修饰符internal,C#默认的类访问修饰符,修饰类或类成员的访问权限为同一程序集内部。
d.public修饰符public,公共访问权限修饰符,修饰类或类成员的访问权限没有任何限制。
大多数情况下须谨慎使用public修饰符,因为滥用将影响类的封装性,并且带来安全隐患。
e.protected internal修饰符protected internal修饰符,修饰类或类成员的访问权限在子类中或同一程序集内。
如果要声明成员只能被同一程序集内的派生类(子类)访问,则应首先设置所属类为internal,成员设置为protected 。
2.共3小题,共9分。
a). 什么是装箱(boxing)和拆箱(unboxing)?请简述.NET对于装箱及拆箱的具体实现方式。
(5分)b). 在下面的例子中,哪些语句用到了装箱拆箱?你能对最后一句代码进行优化提高它的效率吗? (2分)public static void Main(){Int32 v = 5;Object o = v;v = 123;Console.WriteLine(v + ", " + (Int32)o);}c). 你知道.NET对拆箱有什么要求吗?下面的代码可以正确运行吗?如果不行该如何修改? (2分)static void Main(string[] args){int a = 1;object o = a;double b = (double)o;}答案:a.装箱和拆箱:任何值类型、引用类型可以和object(对象)类型之间进行转换。
装箱转换是指将一个值类型隐式或显式地转换成一个object类型,或者把这个值类型转换成一个被该值类型应用的接口类型(interface-type)。
把一个值类型的值装箱,就是创建一个object实例并将这个值复制给这个object,装箱后的object对象中的数据位于堆中,堆中的地址在栈中。
被装箱的类型的值是作为一个拷贝赋给对象的b.用到了3次装箱1次拆箱,其中最后一句代码有2次装箱1次拆箱。
优化后如下:public static void Main(){Int32 v = 5;Object o = v;v = 123;Console.WriteLine(v + ", " + o);}c.拆箱时只能转化成装箱时的值,否则会报类型转化错误的异常,正确写法如下:static void Main(string[] args){int a = 1;object o = a;double b = (double)(int)o;}3.共4小题,共14分。
a). 请从构造函数、继承方式、内存管理、参数传递等角度列举结构体(struct)和类(class)的区别。
(8分)b). 如果你要创建一个如下的点对象, 你倾向于用结构体还是类, 为什么?(2分)Point{double X;double Y;}c).如果是下面的点对象, 你倾向于用结构体还是类,为什么?(2分)Point{double X;double Y;double Z;int Color;}d,如果是下面的对象呢,你会选择用结构体还是类,为什么?(2分)Point{double X;double Y;MoveTo(double x, double y);}答案:在构造函数方面:结构不能声明默认构造函数(没有参数的构造函数),但是类可以。
类的构造函数可以只给部分成员变量赋值,但是结构体的构造函数必须给所有变量赋值。
构造类对象时.NET需要1,在托管堆分配内存;2,初始化一些额外的变量(方法表指针和同步块索引,即Method Table Pointer & SyncBlockIndex);3,调用构造函数。
新建一个结构体只是在栈中非配内存并初始化其成员值。
继承方式方面:结构不能继承或被继承,类可以。
内存管理方面:结构是值类型,类是引用类型。
因此相应的,类对象的内存分配在堆上,受GC管理,结构体对象的内存分配在栈上,不受GC管理。
参数传递方面:因为结构是值类型,类是引用类型,因此一种是按照值拷贝方式传递,一种是引用方式传递。
其他方面:略b. 结构体, < 16 bytesc. 类, > 16 bytesd. 类, not immutable, MoveTo() method will change the point.关于学生自己设计的是类还是结构体,理由可以参考如下:∙堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些∙结构表示如点、矩形和颜色这样的轻量对象,例如,如果声明一个含有1000 个点对象的数组,则将为引用每个对象分配附加的内存。
在此情况下,结构的成本较低。
∙在表现抽象和多级别的对象层次时,类是最好的选择∙大多数情况下该类型只是一些数据时,结构时最佳的选择∙结构体的数据量要小(<16 字节), 以维持拷贝时的高效∙结构体应该是不可变的(immutable)4.共3小题,共6分。
a). String是引用类型还是值类型?(1分)b).下面代码执行结果是什么?为什么会有这样的结果。
(2分)c).字符串操作是一个非常常见但又代价较大的操作,你知道.NET为字符串操作做了哪些优化吗?比如说,你知道string.Intern()这个方法的含义吗?(3分)static void Main(string[] args){string b = "world";f(b);Console.WriteLine(b);}static void f(string a){a = "hello";}答案:a,string属于引用类型,b,执行结果为”world”, 这是因为调用函数f时,b的地址以值拷贝的方式复制给a, a在函数内被指向了新的地址,但其原地址上的内容并没有被改变,因此函数结束之后a的改变不会影响b.C, .NET中所有非动态生成的字符串都被预编译在元数据中,不可改变(immutable),这一点与值类型很相似。
如果两个字符串变量有同样的值,那么它们的地址一定相等。
这样很方便进行字符串的比较操作。
Intern方法就是将动态生成的字符串进行留用,这样下次使用的时候就直接返回留用后的字符串的地址。
采取了Dispose()模式来确保类中包含的资源能够被有效释放.请在下面的代码中增加Dispose()模式,确保资源bitmap可以被有效释放。
(8分)public class MyClass{private Bitmap bitmap;public MyClass(): this(null){ }public MyClass(Bitmap bitmap){this.bitmap = bitmap;}答案:public class MyClass : IDisposable{private Bitmap bitmap;public MyClass(): this(null){ }public MyClass(Bitmap bitmap){this.bitmap = bitmap;}public void Dispose() (3分){this.Dispose(true);GC.SuppressFinalize(this);}private void Dispose(bool disposing) (2分){if (bitmap != null){bitmap.Dispose();}}~MyClass() (3分){Dispose(false);}}6.什么是Web Control的生命周期?请至少按顺序举出3个步骤,并说明他们的作用。
(6分)PreInit: 检查IsPostBack属性。
创建或重建动态添加的控件。
Init: 读取或者初始化控件属性InitComplete: 在初始化完成后触发.LoadViewState: 恢复页面的view State.PreLoaded: 在页面加载之前触发.Load: 在此设置控件属性并建立数据库连接LoadComplete: 在页面加载之后触发SaveViewState: 保存当前控件的状态到ViewStateCollection中PreRender, 使用这个方法在渲染控件前对页面控件内容进行最后的修改。
Render: 将当前控件渲染为浏览器可认的HTML.Unload: 使用这个事件进行控件的资源释放。
如释放数据库连接等7.简述中Session, Application, ViewState的作用和区别,并举例说明何时使用。
(6分)答案:Session:当用户在web应用程序中浏览页面时,为当前用户存储变量。
比如一个Web程序需要在页面间共享一个变量。
如登陆后需要保存用户状态,并在其他页面共享此状态。
Application: 可以在整个web应用程序中跨用户的共享信息。
比如一个投票系统可以用Application来记录用户的投票之和。
ViewState: 一个服务端空间的ViewState是指其包含的所有属性。
比如一个ASPX页面的属性或变量等。
8.2小题,6分。
a). 什么是抽象方法,什么是虚方法,两者有什么区别?(4分)b). 现有基类Polygon, 其下有两个子类Triangle和Rectangle。
基类中包含Polygon中所有顶点的集合以及获取周长的方法GetPerimeter()。
如果是你来设计这个类,GetPerimeter()你倾向使用抽象方法还是虚方法,为什么?(2分)答案:虚方法和抽象方法都可以供派生类重写。
∙虚方法必须有实现部分,并为派生类提供了覆盖该方法的选项抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化。
∙抽象方法只能在抽象类中声明,抽象方法必须在派生类中重写虚方法不是也不必要重写。
其实如果类包含抽象方法,那么该类也是抽象的,也必须声明为抽象的。
∙抽象方法必须在派生类中重写,这一点跟接口类似,虚方法不必。
抽象方法不能声明方法实体而虚方法可以包含抽象方法的类不能实例化,而包含虚方法的类可以实例化应该用虚方法,提供一个默认的实现,可以通过所有点来计算出它周长。