C# 编程基础练习题与答案2.docx
- 文档编号:11639962
- 上传时间:2023-03-29
- 格式:DOCX
- 页数:60
- 大小:57.31KB
C# 编程基础练习题与答案2.docx
《C# 编程基础练习题与答案2.docx》由会员分享,可在线阅读,更多相关《C# 编程基础练习题与答案2.docx(60页珍藏版)》请在冰豆网上搜索。
C#编程基础练习题与答案2
1.面向对象的思想主要包括什么?
答:
个人认为一各程序语言要成为真正的面向对象的程序设计语言,它必须符合下列条件:
1抽象(abstraction)—抽象能够有效地管理一个问题的复杂性,其作法是划分出与该问题相关的一组对象.
2封装(Encapsulation)—封装是指将一个抽象的内部实现隐藏在特定的对象之内.
3多态(polymorphism)—多态会提供相同方法的多种操作方法的多种操作实作.例如,不同的对象都会拥有一个Save方法,但是每一个Save方法会执行不同的操作.
4继承(inheritance)—VisualC#2005最令人兴奋之处就是其继承特性.vc#2005则提供了真正的方法继承,因此您可以重复使用一个类的实例.
2.什么是ASP.net中的用户控件
自己动手作自己的控件来取代.NET提供的控件。
这种控件就是用户控件。
后缀为.ascx
3.什么叫应用程序域?
什么是受管制的代码?
什么是强类型系统?
什么是装箱和拆箱?
什么是重载?
什么是多态?
CTS、CLS和CLR分别作何解释?
应用程序域:
应用程序域(通常是AppDomain)是用于隔离应用程序的虚拟进程。
在同一个应用程序作用域中创建的所有对象(换句话说,从该应用程序的入口点开始沿着对象激活序列的任何地方)都在同一个应用程序域中创建。
多个应用程序域可以存在于一个操作系统进程中,使它们成为隔离应用程序的简便方式。
操作系统进程通过使用各不相同的内存地址空间来提供隔离。
尽管它是有效的,但也是代价昂贵的,并且不能达到大型Web服务器所需要的数量。
与其相比,公共语言运行时通过管理在应用程序域中运行的代码的内存使用来强制进行应用程序隔离。
这样就确保它不会访问应用程序域以外的内存。
需要注意的是,只有类型安全的代码才能以这种方式管理(当在应用程序域中加载不安全代码时,运行时不能保证隔离)。
理解应用程序域:
应用程序域是.NET运行库的逻辑进程表示,操作系统进程可以包含多个应用程序域。
应用程序域具有下列优点:
1、隐藏了进程具体的操作系统信息。
从而允许把.NET放在不同的操作系统下。
2、提供了隔离。
即使运行在同一个进程中的属于不同域的应用程序也不能直接共享全局数据、静态数据或其他资源。
所以,一个应用程序域失败了,也不会影响到同一个进程中的其他应用程序域。
3、对于不需要昂贵的IPC机制的进程,应用程序域允许.NET运行库优化运行在这种进程中的应用程序间的通信。
因为应用程序域是被隔离的,所有.NET对象都会被界定在创建它的应用程序域内。
如果跨应用程序域的边界传递对象引用,该被引用对象就被称为远程对象。
装箱和拆箱
在C#中的有两种类型的变量:
值类型和引用类型。
当值类型和引用类型相互转化时,会发生装箱和拆箱的过程。
这里有一点要声明:
经过拆箱或装箱的对象会多出它自己一份拷贝。
它和它的拷贝不在一个存储区域。
这也是值类型和引用类型的区别所在。
值类型总是在栈中,而引用类型总是在托管堆中。
(目前J2SE5.0也支持了装箱和拆箱,但是我目前不知道是否和C#一样)。
为了进一步理解看下面例子:
structPoint{
publicintx;
publicinty;
}
staticvoidMain(){
Pointp;
p.x=10;
p.y=20;
Objecto=p;//box.将值类型从栈中拷贝到堆中。
/************************************************************************/
*从托管堆中将对象拷贝到栈中。
/************************************************************************/
Pointp2=(Point)p;
Console.WriteLine(“p2.x:
="+p2.x+“p2.y:
=“+p2.x);
p.x=16;
p.y=34;
Console.WriteLine(“p2.x:
="+p2.x+“p2.y:
=“+p2.x);
Console.WriteLine(“p.x:
="+p.x+“p.y:
=“+p.x);
}
输出结果为:
p2.x:
=10;p2.y=20;
p2.x:
=10;p2.y=20;
p.x:
=16;p.y=34;
可知,变量经过拆箱/装箱后,得到是自己的另一份拷贝。
装箱和取消装箱的概念是C#的类型系统的核心。
它在“值类型”和“引用类型”之间的架起了一座桥梁,使得任何“值类型”的值都可以转换为object类型的值,反过来转换也可以。
装箱和取消装箱使我们能够统一地来考察类型系统,其中任何类型的值最终都可以按对象处理
多态
一.形象理解
两条理解的原则:
(1)一个派生类对象可以被声明成一个基类,或者是一个基类指针可以指向一个派生类对象:
//c++code
BaseClass*p;
DerivedClassobj;
p=&obj;
//C#code
BaseClassobj=newDerivedClass();
(2)把一个对象看做是一个独立的个体,调用对象的public成员函数实际上是给这个对象发送一个消息,采取什么样的动作完全由对象自己决定。
Shape是基类,Circle和Line是从Shape继承出来的,Shape有draw()方法,Circle与Line分别自己定义了自己的draw()方法,在下面的代码里:
//JavaCode
staticvoidfunc(Shapes)
{
s.Draw();
}
如果发生了这样的调用:
Linel=newLine();
Circlec=newCircle();
func(l);
func(c);
一个Circle和一个Line被当做Shape传到函数里去了,然后调用Draw(),会发生什么情况?
因为对象是独立的个体,在func()里,这两个对象被分别传递了Draw()消息,叫它们绘制自己吧,于是他们分别调用了自己类里定义的Draw()动作。
通过这两条原则我们可以理解上面的多态。
正是由于多态,使得我们不必要这样去做:
IF你是一个CircleTHEN调用Circle的Draw()
ELSEIF你是一个LineTHEN调用Line的Draw()
ELSE…
我们只要给这个被声明成为Shape的对象发送Draw消息,怎么样去Draw就由对象自己去决定了。
二.一切皆因虚函数
先看看实现多态的基本条件:
(1)基类含有虚函数
(2)继承类把这个虚函数重新实现了
(3)继承类也可能没有重新实现基类的所有虚函数,因此对于这些没有被重新实现的虚函数不能发生多态。
再看一下几种语言里一些特别的规定:
1.C++:
(1)虚函数用virtual关键字声明。
(2)virtualvoidFunc(para_list)=0;这样的虚函数叫做纯虚函数,表示这个函数没有具体实现。
包含纯虚函数的类叫做抽象类,如果他的继承类没有对这个纯虚函数具体用代码实现,则这个继承类也是抽象类。
抽象类不能被实例话(就是说不能创建出对象)。
(3)继承类重新实现基类的虚函数时,不需要做任何特别的声明。
(4)如果不用virtual关键字修饰,并且在派生类里重新实现了这个方法,这仅仅是一个简单的覆盖,不会发生多态,我们暂称它非多态吧。
2.Java:
(1)Java没有virtual关键字,Java把一切类的方法都认为是虚函数。
(2)继承类重新实现基类的虚函数时,不需要做任何特别的声明。
因此在Java里只要重新实现了基类的方法,并且把继承类对象声明为基类,多态就要发生。
因此Java对多态的条件相对是比较低的。
//JavaCode
classBaseClass
{
publicvoidhello(){};
}
classDerivedClassextendsBaseClass
{
publicvoidhello()
{
System.out.println(“Helloworld!
”);
}
publicstaticvoidmain(Stringargs[])
{
BaseClassobj=newDerivedClass();
obj.hello();
}
}
输入是Helloworld!
。
这样就实现了多态。
(3)虚函数用abstract声明,含有虚函数的类是抽象类,也要用abstract关键字修饰。
//JavaCode
publicabstractAbstractClass
{
publicabstractvoidhello();
//…
}
3.C#:
C#对于多态的编写是最为严格和严谨的。
(1)虚函数用virtual声明。
(2)纯虚函数用abstract声明,含纯虚函数的类是抽象类,必须用abstract关键字修饰。
(3)如果仅仅是覆盖基类的非虚方法,则需要用new关键字声明:
//C#Code
publicclassBaseClass
{
publicvoidhello()
{
System.Console.WriteLine(“Hello,thiscomefromBaseClass”);
}
}
publicclassDerivedClass:
BaseClass
{
publicnewvoidhello()
{
System.Console.WriteLine(“Hello,thisiscomefromDerivedClass”);
}
publicstaticvoidMain()
{
BaseClassobj=newDerivedClass();
obj.hello();
}
}
输出为Hello,thiscomefromBaseClass,也就是说这并没有实现多态(非多态)。
(4)通过virtual–override、abstract–override组合实现多态。
当派生类重新实现基类的虚函数(或纯虚函数)时,必须用override关键字进行修饰。
//C#Code
publicabstractclassAbsBaseClass
{
publicabstractvoidhello();
}
publicclassDerivedClass:
AbsBaseClass
{
publicvoidhello()
{
System.Console.WriteLine(“Helloworld!
”);
}
publicstaticvoidSayHello(AbsBaseClassobj)
{
obj.hello();
}
publicstaticvoidMain()
{
DerivedClass_obj=newDerivedClass();
DerivedClass.SayHello(_obj);
}
}
输出为Helloworld!
三.多态的反溯
继承类对象在发生多态时,并是不完全抛开基类不管的,它会去查看基类的虚函数列表,在这个列表的范围内才会发生多态。
让我们来看一个比较复杂的例子:
//JavaCode
classA
{
protectedvoidhello(Objecto)
{
System.out.println("A-Object");
}
}
classBextendsA
{
protectedvoidhello(Strings)
{
System.out.println("B-String");
}
protectedvoidhello(Objecto)
{
System.out.println("B-Object");
}
};
classC
{
publicstaticvoidmain(Stringargs[])
{
Objectobj=newObject();
Stringstr="ABC";
Aa=newB();
a.hello(obj);
a.hello(str);
}
};
输出结果为:
B–Object
B–Object
正如上面所说的,由于基类里没有参数类型为String的虚函数,因此B的hello(String)方法不参与多态。
调用a.hello(str)时,由于String是Object的继承类,因此这个str被作为一个Object传入了B的hello(Object),这一点正如我们的原则一所述。
四.接口——仅仅是更抽象的抽象类
接口是类的协定,但由于接口又参与多态性,从这一点说,我们认为它是更为抽象的抽象类:
CTS、CLS和CLR
.NET结合Java和COM解决方案两者优点来解决互操作性问题。
类似于COM定义的标准二进制格式,.NET定义了一个称为通用类型系统CommonTypeSystem(CTS)的类型标准。
这个类型系统不但实现了COM的变量兼容类型,而且还定义了通过用户自定义类型的方式来进行类型扩展。
任何以.NET平台作为目标的语言必须建立它的数据类型与CTS的类型间的映射。
所有.NET语言共享这一类型系统,实现它们之间无缝的互操作。
该方案还提供了语言之间的继承性。
例如,用户能够在VB.NET中派生一个由C#编写的类。
很显然,编程语言的区别不仅仅在于类型。
例如,一些语言支持多继承性,一些语言支持无符号数据类型,一些语言支持运算符重载。
用户应认识到这一点,因此.NET通过定义公共语言规范(CLS:
CommonLanguageSpecification),限制了由这些不同引发的互操作性问题。
CLS制定了一种以.NET平台为目标的语言所必须支持的最小特征,以及该语言与其他.NET语言之间实现互操作性所需要的完备特征。
认识到这点很重要,这里讨论的特征问题已不仅仅是语言间的简单语法区别。
例如,CLS并不去关心一种语言用什么关键字实现继承,只是关心该语言如何支持继承。
CLS是CTS的一个子集。
这就意味着一种语言特征可能符合CTS标准,但又超出CLS的范畴。
例如:
C#支持无符号数字类型,该特征能通过CTS的测试,但CLS却仅仅识别符号数字类型。
因此,如果用户在一个组件中使用C#的无符号类型,就可能不能与不使用无符号类型的语言(如VB.NET)设计的.NET组件实现互操作。
这里用的是“可能不”,而不是“不可能”,因为这一问题实际依赖于对non-CLS-compliant项的可见性。
事实上,CLS规则只适用于或部分适用于那些与其他组件存在联系的组件中的类型。
实际上,用户能够安全实现含私有组件的项目,而该组件使用了用户所选择使用的.NET语言的全部功能,且无需遵守CLS的规范。
另一方面,如果用户需要.NET语言的互操作性,那么用户的组件中的公共项必须完全符合CLS规范。
让我们来看下面的C#代码:
publicclassFoo
{
//Theuint(unsignedinteger)typeisnon-CLScompliant.
//Butsincethisitemisprivate,theCLSrulesdonotapply.
privateuintA=4;
//Sinceshisuintmemberispublic,wehaveaCLS
//complianceissue.
publicuintB=5;
//ThelongtypeisCLScompliant.
publiclongGetA()
{
returnA;
}
}
最后一个C是公共语言运行库CommonLanguageRuntime(CLR)。
简单地说,CLR是CTS的实现,也就是说,CLR是应用程序的执行引擎和功能齐全的类库,该类库严格按照CTS规范实现。
作为程序执行引擎,CLR负责安全地载入和运行用户程序代码,包括对不用对象的垃圾回收和安全检查。
在CLR监控之下运行的代码,称为托管代码(managedcode)。
作为类库,CLR提供上百个可用的有用类型,而这些类型可通过继承进行扩展。
对于文件I/O、创建对话框、启动线程等类型——基本上能使用WindowsAPI来完成的操作,都可由其完成。
让我们正确看待“3C”。
开发人员在构建自己的分布式应用程序时,因为用户在编程时将直接面对CLR,应将主要精力放在学习了解CLR上,而不是CTS和CLS。
而对于希望以.NET平台为目标的语言和工具开发商来说,就需要深入理解CTS和CLS。
互操作性组件是分布式应用的关键,因此理解.NET如何通过定义公共类型实现这一目标,也就显得十分重要。
4.列举一下你所了解的XML技术及其应用
5.值类型和引用类型的区别?
写出C#的样例代码Ref与Out的区别。
Ref与Out的区别是
相同点:
1.使参数按引用传递,注意这里的“引用”与我们通常说的“对象引用”不一样,可以形象的理解为,类似于C/C++中传指针(地址)。
2.效果是,被调用方对该参数的修改会反映到该变量中,即当调用结束返回时,调用方看到的是修改过的值。
3.方法定义和调用方法都必须显式使用ref或者out关键字
不同点:
1。
传递到ref的参数必须最先初始化,即由调用方显式初始化。
2。
传递到out的参数在传递之前不需要显式初始化,但需要调用方在方法返回之前必须对其赋值。
使用场景:
关于重载说明:
1。
ref和out关键字在运行时的处理方式不同,但在编译时的处理方式相同。
因此,如果一个方法采用ref参数,而另一个方法采用out参数,则无法重载这两个方法。
2。
但是,如果一个方法采用ref或out参数,而另一个方法不采用这两类参数,则可以进行重载。
使用ref前必须对变量赋值,out不用。
out的函数会清空变量,即使变量已经赋值也不行,退出函数时所有out引用的变量都要赋值,ref引用的可以修改,也可以不修改。
区别可以参看下面的代码:
usingSystem;
classTestApp
{
staticvoidoutTest(outintx,outinty)
{//离开这个函数前,必须对x和y赋值,否则会报错。
//y=x;
//上面这行会报错,因为使用了out后,x和y都清空了,需要重新赋值,即使调用函数前赋过值也不行
x=1;
y=2;
}
staticvoidrefTest(refintx,refinty)
{
x=1;
y=x;
}
publicstaticvoidMain()
{
//outtest
inta,b;
//out使用前,变量可以不赋值
outTest(outa,outb);
Console.WriteLine("a={0};b={1}",a,b);
intc=11,d=22;
outTest(outc,outd);
Console.WriteLine("c={0};d={1}",c,d);
//reftest
intm,n;
//refTest(refm,refn);
//上面这行会出错,ref使用前,变量必须赋值
into=11,p=22;
refTest(refo,refp);
Console.WriteLine("o={0};p={1}",o,p);
}
}
6.ADO.net中常用的对象有哪些?
分别描述一下。
Connection 对象
Command与DataReader 对象
DataSet 与DataAdapter
7.如何理解委托?
单从委托的概念上来讲,很简单,就是对方法的引用,包括静态方法和对象实例的方法,有点类似C语言中的方法指针,不过方法指针只能引用静态方法!
而且委托是类安全的!
一句话来概括:
委托就是方法的入口!
调用委托就是调用方法,可能有人会说既然调用委托就是调用方法,那为什么不直接调用方法了,干吗非得通过委托来调用!
一开始我也是这么想,包括现在也还有这个疑惑,个人觉得,如果撇开事件来说,委托除了匿名方法名称之外,没有具体实质型的好处!
意思就是说我们如果不是在事件里用委托,和直接调用方法是没有本质区别的!
至于委托的声明格式,其基本语法为:
修饰符delegate返回类型代理名称(参数列表)
比如说publicdelegatevoidMyDelegate(inti);
实例化的时候给它赋值一个方法(实例或静态方法)名就可以了MyDelegateMD=newMyDelegate(类中的方法名)
委托还可以实现多重委托,可以用Combine方法讲不同代理组在一起,也可以用Remove方法从多重代理中除去一个代理,不过在我们实际使用可以不需要这么麻烦,用+,-可以达到这个目的!
比如说:
MyDelegateMD=newMyDelegate(方法1);
MyDelegateMD2=newMyDelegate(方法2);
MyDelegateMD3=MD+MD2;
也可以这么写
MyDelegateMD3+=newMyDelegate(方法1);
MyDelegateMD3+=newMyDelegate(方法2);
执行代理MD3就是调用方法1和方法2;
多重代理个人觉得在事件委托中用处很大,因为在实际对象中,一个方法执行会触发多个方法相继执行,这个时候会需要多重代理这个概念!
使用多重代理的时候要注意方法返回类型必须是void类型的!
PS:
在代理类中有2个属性,一个是Method,表示所应用方法的名称!
另一个是Target,表示实例方法所在对象的类型,如果是静态方法,则返回NULL
事件(Event)
正如我上面说的那样,如果撇开事件,委托好像没有多大用途,但也只是假设,委托就是为事件服务的,两者是分不开的.言事件比言委托!
在C#中,事件和属性,方法一样,是类的一种成员,通过事件的使用,可以在类的对象发生某种变化,通过事件,来触发某些方法来处理事件的发生!
举个通俗的例子,在ASP.NET的窗体生成种,拖进来一个按钮,双击它,页面后台会自动生成按钮事件的方法名称,编程人员只要在该方法体内写上具体方法即可!
那么按钮类里面是怎么实现的了?
具体是这样的,在按钮类里面声明了一个事件委托:
publicenvetEventHandlerClick;
并写了一个触发该事件的方法:
protectedvirtualvoidOnClick(EventArgese)
{
if(Click!
=Null)
{
Click(this,e
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 编程基础练习题与答案2 编程 基础 练习题 答案