.NET面试题 - 4
Q1. C#中 property 与 attribute的区别,他们各有什么用处,这种机制的好处在哪里?
答:Property用来封装类的成员,可以通过逻辑限制对成员的读写及可访问性。而Attribute是一种标记类,通过反射获取Attribute信息可以获得一个类的部分特性。
Q2. 讲一讲你理解的web service,在dot net framework中,怎么很好的结合xml?
答:Web Service通过SOAP协议(本质是XML数据)来传输信息,对比普通Web应用来看,发布信息都是需要Web Server。只是发布信息有所不同,普通Web应用发布的是Html/XHTML的数据。而Web Service发布的是Xml类型符合SOAP协议的数据。(当然WebService也可以使用JSON等来传输数据)
Q3. C#可否对内存进行直接的操作?
答:是可以的。可以使用C#中所谓的非安全代码,在这类代码中可以使用指针以操作内存。非安全代码需放置在unsafe块中。
Q4. 用Visual C++ 6.0编写的代码(unmanaged code),如何在CLR下和其他.Net Component结合?
答:对于非托管程序集可以使用.NET中的互操作性的功能。从MSDN摘录了一个简要的步骤:
1.标识 DLL 中的函数。最低限度上,必须指定函数的名称和包含该函数的 DLL 的名称。
2.创建用于容纳 DLL 函数的类。可以使用现有类,为每一非托管函数创建单独的类,或者创建包含一组相关的非托管函数的一个类。
3.在托管代码中创建原型。[C#] 使用 DllImportAttribute 标识 DLL 和函数。用 static 和 extern 修饰符标记方法。
4.调用 DLL 函数。像处理其他任何托管方法一样调用托管类上的方法。传递结构和实现回调函数属于特殊情况。
Q5:维护数据库的完整性、一致性、你喜欢用触发器还是自写业务逻辑?为什么
答:自己写业务逻辑,可控性更高。同时性能也比触发器要好一些。
Q6:ADO.NET相对于ADO等主要有什么改进?
答:ADO.NET中实现了两套类,连接类与非连接类。非连接类实现了一套内存中的关系数据模型,将数据存于这样的dataset中可以减少访问数据库的次数。另外连接类应用了Provider模式,可以方便的连接到多少数据源。
Q7:ASP.NET与ASP相比,主要有哪些进步?
答:实现了一套WebForm框架,有完整的生命周期模型,使开发效率大大提高,而且编译执行的方式比ASP解释执行的效率大大提高。通过自定义控件的方式也实现了一种较高层次的复用。
Q8:C#中的委托是什么?事件是不是一种委托?
答:委托也是一种引用类型,其表示一种可以被调用的方法的签名的规范。委托的实例是表示到与其匹配的方法的引用。事件就是委托的一种。
Q9:描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
答:MSDN中对索引器的说明:索引器允许类或结构的实例按照与数组相同的方式进行索引。索引器类似于属性,不同之处在于它们的访问器采用参数。 下面先看一个简单的索引器:
1 class SampleCollection<T> 2 { 3 private T[] arr = new T[100]; 4 public T this[int i] 5 { 6 get 7 { 8 return arr[i]; 9 } 10 set 11 { 12 arr[i] = value; 13 } 14 } 15 }
索引器使用方式:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 SampleCollection<string> stringCollection = new SampleCollection<string>(); 6 stringCollection[0] = "Hello, World"; 7 System.Console.WriteLine(stringCollection[0]); 8 } 9 }
MSDN中对索引器设计的一些要求:
索引器概述
索引器使得对象可按照与数组相似的方法进行索引。 get 访问器返回值。set 访问器分配值。 this 关键字用于定义索引器。 value 关键字用于定义由 set 索引器分配的值。 索引器不必根据整数值进行索引,由您决定如何定义特定的查找机制。 索引器可被重载。 索引器可以有多个形参,例如当访问二维数组时。
由上可以看出索引也可以字符串等其它类型。
最后给出一个较为复杂的索引器的例子。(摘自:http://www.cnblogs.com/jiang_jiajia10/archive/2009/03/13/1410798.html)
1 class Program1 2 { 3 static void Main(string[] args) 4 { 5 ScoreIndex s = new ScoreIndex(); 6 s["张三", 1] = 90; 7 s["张三", 2] = 100; 8 s["张三", 3] = 80; 9 s["李四", 1] = 60; 10 s["李四", 2] = 70; 11 s["李四", 3] = 50; 12 Console.WriteLine("张三课程编号为1的成绩为:" + s["张三", 1]); 13 Console.WriteLine("张三的所有成绩为:"); 14 ArrayList temp; 15 temp = s["张三"]; 16 foreach (IndexClass b in temp) 17 { 18 Console.WriteLine("姓名:" + b.Name + "课程编号:" + b.CourseID + "分数:" + b.Score); 19 } 20 Console.ReadKey(); 21 } 22 } 23 24 class IndexClass 25 { 26 private string _name; 27 private int _courseid; 28 private int _score; 29 public IndexClass(string _name, int _courseid, int _score) 30 { 31 this._name = _name; 32 this._courseid = _courseid; 33 this._score = _score; 34 } 35 public string Name 36 { 37 get { return _name; } 38 set { this._name = value; } 39 } 40 public int CourseID 41 { 42 get { return _courseid; } 43 set { this._courseid = value; } 44 } 45 public int Score 46 { 47 get { return _score; } 48 set { this._score = value; } 49 } 50 } 51 52 class ScoreIndex 53 { 54 private ArrayList arr; 55 public ScoreIndex() 56 { 57 arr = new ArrayList(); 58 } 59 public int this[string _name, int _courseid] 60 { 61 get 62 { 63 foreach (IndexClass a in arr) 64 { 65 if (a.Name == _name && a.CourseID == _courseid) 66 { 67 return a.Score; 68 } 69 } 70 return -1; 71 } 72 set 73 { 74 arr.Add(new IndexClass(_name, _courseid, value)); //arr["张三",1]=90 75 } 76 } 77 //重载索引器 78 public ArrayList this[string _name] 79 { 80 get 81 { 82 ArrayList temp = new ArrayList(); 83 foreach (IndexClass b in arr) 84 { 85 if (b.Name == _name) 86 { 87 temp.Add(b); 88 } 89 } 90 return temp; 91 } 92 } 93 }
Q10:C#中要使一个类支持foreach遍历,实现过程怎样?
答:这个问题参见我的这篇随笔:http://www.cnblogs.com/lsxqw2004/archive/2009/10/15/1583829.html
总结一句话:实现IEnumerable接口的GetEnumerator方法(此方法返回个实现IEnumerator接口的对象),这个方法返回的对象是迭代器的重点。
Q11:写一个HTML页面,实现以下功能,左键点击页面时显示"您好",右键点击时显示"禁止右键"。并在2分钟后自动关闭页面。
答:
JavaScript写法:
1 <body onload="init()"> 2 3 <script type="text/javascript"> 4 function init() { 5 document.onclick = showalert; 6 document.oncontextmenu = new Function("event.returnValue=false"); 7 window.setTimeout(function() { window.close(); }, 2 * 60 * 1000); 8 } 9 10 function showalert() { 11 alert("您好"); 12 } 13 </script> 14 15 <form id="form1" runat="server"> 16 </form> 17 </body>
Q12: 简单描述XMLHTTP、WebService的特点、作用。
XmlHttp是一种置于浏览器内的组件,其为浏览器与服务器间进行异步调用来服务。
Q13:接口和抽象类有什么区别?你选择使用接口和抽象类的依据是什么?
答:接口不可以包括成员及函数实现,抽象类可以,接口可以多继承,抽象类不可以。
接口主要定义一个协定。实现接口的类或结构必须遵守其协定。抽象类应主要用于关系密切的对象,如果要设计大的功能单元,则使用抽象类。
Q14:自定义控件和一般用户控件的异同?如果要用这两者之一,你会选择哪种?为什么
答:自定义控件一般就是将现有的服务器控件整合在一个扩展名为.ascx文件中,其作用是使一组服务器控件的复用性增高。而用户空间就是完全创建一个新的服务器控件,我们可以最大化的实现我们想要的功能,可以像现有服务器控件一样放入工具箱中。
Q15:大概描述一下ASP.NET服务器控件的生命周期。
答:
-
实例化(Instantiate):
控件被页面或另一个控件通过调用它的构造器所实例化。这个步骤之后所列出的阶段,仅当控件加入控件树中才会发生。
-
初始化(Initialize):
初始化在传入 Web 请求生命周期内所需的设置。在此阶段,控件树中的页面和全部控件通过默认方式来调用OnInit方法。开发人员可以通过重载OnInit方法,为控件提供初始化逻辑。在其生命周期的这个时候,控件能够安全地访问其置于Controls集合中的子控件,但是它不能访问控件层次中的父控件或其他层次更高的控件(如页面)
-
开始跟踪视图状态(Begin Tracking View State):
这个阶段发生在初始化阶段的末尾。在此阶段页面自动调用TrackViewState方法。TrackViewState方法保证在此阶段之后,使用ViewState字典属性而产生的变化保存在控件视图状态中。在大多数情况下,Control基类提供的TrackViewState方法实现已经足够了,只有在控件定义了复杂属性时,才必须重载TrackViewState方法。
-
加载视图状态(仅用于回传过程)(Load View State (postback only)):
这个阶段发生在回传时,而不是初始请求过程中,在此阶段结束时,就会自动填充控件的 ViewState 属性。控件可以重写 LoadViewState 方法的默认实现,以自定义状态还原。
-
加载回传数据(仅用于回传过程,为可选项)(Load Postback Data(postback only, optional)):
只有在控件通过实现IPostBackDataHandler接口参与了回传数据处理时,这个阶段才发生在回传中。TextBox控件就是一个例子。在这个阶段中,控件必须从已发送的表单数据中,通过实现IPostBackDataHandler接口的LoadPostData方法更新其状态。
-
加载(Load):
直到此阶段开始,控件树中的所有控件都已被初始化,并恢复到它们在先前周期最后的状态。OnLoad方法会执行所有请求共有的操作,如设置数据库查询。此时,树中的服务器控件已创建并初始化、状态已还原并且窗体控件反映了客户端的数据。如果需要实现仅在页面初始请求中执行的逻辑,那么实现该逻辑时,应该检查页面的IsPostBack属性
-
引发修改事件(仅用于回传过程,为可选项)(Raise Changed Events(postback only, optional)):
只有在控件通过实现IPostBackDataHandler接口参与了回传数据处理时,这个阶段才发生在回传中。在此阶段中,控件通过引发事件(如TextBox的TextChanged事件)作为一种信号-----其状态由于回传而改变(引发更改事件以响应当前和以前回发之间的状态更改)。为了参与此阶段,控件必须实现IPostBackDataHandler接口的RaisePostDataChangedEvent方法。
-
引发回传事件(仅用于回传过程,为可选项)(Raise Postback Events(postback only, optional)):
只有在控件通过实现IPostBackEventHandler接口参与了回传数据处理时,这个阶段才发生在回传中。在此阶段可以通过实现IPostBackEventHandler接口的RaisePostBackEvent方法来实现逻辑,以便把客户端事件映射到服务器端事件。
-
预生成(PreRender):
在此阶段中,应该通过重载OnPreRender方法,执行在生成控件之前所需要的任何工作。在生成输出之前执行任何更新,可以保存在预生成阶段对控件状态所做的更改,而在生成阶段所对应的更改则会丢失。
-
保存视图状态(Save View State):
如果控件不维持状态,或者它为保存其所有状态信息而使用ViewState字典,那么不必在此阶段期间实现任何附加逻辑。在此阶段期间,页面框架会自动保存ViewState字典。如果需要自定义状态管理,必须通过重载SaveViewState方法来实现自定义状态恢复,这种方法只被EnableViewState属性为true的控件所调用。在此阶段以后任何控件的改变都不会保存在控件的视图状态中。
-
生成(Render):
通过这种方法,控件在输出流上通过重载Control的Render方法或WebControl类的rendering方法中的一种,来写标记文本。
-
卸载(Unload):
在此阶段中,页面通过实现Page_Unload方法,来执行清除工作。作为控件开发者,应该重载Dispose方法来执行清除。
(摘自:http://blog.csdn.net/grgufo/archive/2008/02/20/2110123.aspx)
一.填空题
1.c#中的三元运算符是__?:__?
2.当整数a赋值给一个object对象时,整数a将会被__装箱__?
3.类成员有_____种可访问形式?
分普通成员/方法还有静态成员/方法:
普通成员/方法 类内部:this.Member/this.Method() 类外部:obj.Member/obj.Method()
静态成员/方法 类内部:Member/Method() 类外部:ClassName.Member/ ClassName.Method()
4.public static const int A=1;这段代码有错误么?是什么? const不能用static修饰
5.float f=-123.567F;
int i=(int)f;
i的值现在是_123_?
(说明:无论正负,无论小数部分是否大于.5,都是把小数部分砍掉而不考虑什么四舍五入。)
6.利用operator声明且仅声明了"==",有什么错误么?
重载==需要同时重载!=。(个人想法)
7.委托声明的关键字是__delagete _?
8.用sealed修饰的类有什么特点?表示密封类,不能继承
9.在Asp.net中所有的自定义用户控件都必须继承自_______?
自定义ASP.NET服务器控件派生自System.Web.UI.WebControls.WebControl类;
Web用户控件派生自System.Web.UI.UserControl类。
10.在.Net中所有可序列化的类都被标记为_[Serializable]_?
11.在.Net托管代码中我们不用担心内存漏洞,这是因为有了_GC_?
CLR中的自动垃圾回收机制
12.下面的代码中有什么错误吗?_______
1 class A 2 { 3 public virtual void F() 4 { 5 Console.WriteLine("A.F"); 6 } 7 } 8 abstract class B : A 9 { 10 public abstract override void F(); 11 //new public abstract void F(); 12 }
按编译看没有什么错误。。。
13.当类T只声明了私有实例构造函数时,则在T的程序文本外部,__不可以__(可以 or 不可以)从T派生出新的类, 不可以__(可以 or 不可以)直接创建T的任何实例。
14.下面这段代码有错误么?
1 int i = 0; 2 switch (i) 3 { 4 case 0: 5 CaseZero(); 6 break; 7 case 1: 8 CaseOne(); 9 break; 10 case 2: 11 dufault; //wrong –> dufault: //right 12 CaseTwo(); 13 break; 14 }
15.在.Net中,类System.Web.UI.Page 可以被继承么?
可以(所有WebForm中创建的页面都是源自这个类)
二.简答题
1. 在c#中using和new这两个关键字有什么意义,请写出你所知道的意义?
using:引用命名空间
using:实现将代码包装在一个try{}catch{}finally{}块中。
new:创建对象新实例
new:隐藏基类中方法。
2. 谈谈类和结构的区别?
最重要的区别:类是引用类型、结构是值类型。
使用选择上:较复杂的逻辑放在类中,有大量的成员对象放在结构中。
3. 一个长度为10000的字符串,通过随机从a-z中抽取10000个字符组成。请用c#语言编写主要程序来实现。
1 static void Main() 2 { 3 Random r = new Random(); 4 StringBuilder sb = new StringBuilder(); 5 int index = (int)'a'; 6 for (int i = 0; i < 10000; i++) 7 { 8 int m = r.Next(26) + index; 9 sb.Append(Convert.ToChar(m).ToString()); 10 } 11 sb.ToString(); 12 }
4. 对于这样的一个枚举类型:
1 enum Color : byte 2 { 3 Red, 4 Green, 5 Blue, 6 Orange 7 }
试写一段程序显示出枚举类型中定义的所有符号名称以及它们对应的数值。
解答:
1 string[] ss = Enum.GetNames(typeof(Color)); 2 byte[] bb = (byte[])Enum.GetValues(typeof(Color)); 3 4 for (int i = 0; i < ss.Length; i++) 5 { 6 Console.WriteLine("value:{0}; text:{1}", bb[i], ss[i]); 7 }
5.请在SQL Server中设计表来保存一个树状结构的组织结构图(假设结构图中只有名称这一项内容需要保存),如果我想查询某一职位下的所有职位,用一个存储过程来实现,你有什么思路?
答:深入起来这是一个复杂的问题,可以参考这篇文章:
http://hi.baidu.com/genet/blog/item/fbc5ba0acf73b33bb1351d91.html
一般应用中我们可以采取如下简单的方式:
数据表设计:
(查询语句:
1 SELECT ID, ParentID, Pos, Title 2 FROM Nodes 3 ORDER BY ParentID
)
我们将要设计的存储过程传入一个待查询职位的ID,返回其下所有节点的Title:
1 CREATE PROCEDURE GetTitle 2 @ID int 3 AS 4 BEGIN 5 SELECT Title 6 FROM Nodes 7 WHERE ParentID = @ID 8 END
6.什么叫做SQL注入,如何防止?请举例说明。
答:通过构造非法的SQL的字符串来是系统返回攻击者想要得到的信息,对输入字符串进行严格校验。替换其中的单引号 - '等。
7.下面这段代码输出什么?为什么?
1 int i=5; 2 int j=5; 3 if (Object.ReferenceEquals(i,j)) 4 Console.WriteLine("Equal"); 5 else 6 Console.WriteLine("Not Equal");
答:输出Not Equal,ReferenceEquals比较两个引用类型对象的引用是否相同,对于值类型要先装箱再比较,所以总是返回false(装箱操作引用不会相同)。
8. 写一个实现对一段字符串翻转的方法,附加一些条件,如其中包括","、".",对其设计测试用 例 。
1 string strfrist = "abcdef"; 2 int len = strfrist.Length; 3 string aa = ""; 4 5 for (int i = len - 1; i >= 0; i--) 6 { 7 aa += strfrist.Substring(i, 1); 8 } 9 string resultstr = aa.ToString();
测试用例不了解
9. 对一支纸杯设计测试用例(可以是广义的杯,不一定是某一支特定功能的杯)
答:漏不漏,超过最大容量溢出的保护。仍然对测试用例不了解。
10. 什么是反射?
答:反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性(Attribute),可以利用反射对它们进行访问。
11. 用Singleton设计模式编写一段代码。
1 public class Singleton 2 { 3 private static Singleton instance = null; 4 5 public static Singleton getInstance() 6 { 7 if (instance == null) 8 instance = new Singleton(); 9 return instance; 10 } 11 }
12. C#中的垃圾回收机制是怎样的?
答:垃圾回收机制需要应用程序不时地暂停从而释放不再使用的内存,垃圾回收机制假设了以下情况:
1.被分配内存空间的对象最有可能被释放。
2.生命期最长的对象释放的可能性最小。
3.同时被分配内存的对象通常是同时使用,将它们彼此相连有助于提高缓存性能和回收效率 。
C#中的回收器是分代的垃圾回收器(Gererational Garbage Collector) ,它将分配的对象分为3个代。最常用的对象在第一代,每次回收后还在使用的对象将被放入下一代,直至被放入第二代。另外回收器负责引用对象的回收,不负责值类型对象的回收。
13. 什么是Application Pool?
答:应用程序池是将一个或多个应用程序链接到一个或多个工作进程集合的配置。因为应用程序池中的应用程序与其他应用程序被工作进程边界分隔,所以某个应用程序池中的应用程序不会受到其他应用程序池中应用程序所产生的问题的影响。
14. Remoting在客户端服务器怎么实现?
答:以Remoting的升级技术WCF来说,服务器端通过终结点(定义了服务的地址,协议,契约)来发布,客户端订阅这个服务,定义代理(其中也包括与终结点相同的地址,协议,契约等信息)来访问服务。
15.什么是虚函数?
一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死了的!而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数。
检查过程如下:
1、当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即声明类,看所调用的函数是否为虚函数;
2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。
3、在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重写了该虚函数的父类为止,然后执行该父类里重写后的函数。
给出个示例说明上述问题:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 C c = new C(); 6 } 7 } 8 9 public class A 10 { 11 public A() 12 { 13 Console.WriteLine("DEBUG: A constructing"); 14 this.GetYear(); 15 } 16 17 public virtual void GetYear() 18 { 19 Console.WriteLine("A"); 20 } 21 } 22 23 public class B : A 24 { 25 public B() 26 : base() 27 { 28 Console.WriteLine("DEBUG: B constructing"); 29 this.GetYear(); 30 } 31 32 public override void GetYear() 33 { 34 Console.WriteLine("B"); 35 } 36 } 37 38 public class C : B 39 { 40 public C() 41 { 42 Console.WriteLine("DEBUG : C constructing"); 43 this.GetYear(); 44 } 45 46 public override void GetYear() 47 { 48 Console.WriteLine("C"); 49 } 50 }
参考:
http://www.cnblogs.com/piccolo/articles/128606.html
http://blog.csdn.net/thebesghost/archive/2006/11/23/1408433.aspx
16.什么是抽象函数?
抽象函数没有执行代码,必须在非抽象的派生类中重写。显然,抽象函数也是虚拟的(但也不需要提供virtual关键字)。如果类包含抽象函数,该类将也是抽象的,也必须声明为抽象的。
17.使用什么工具来调用存储过程。
答:ADO.NET2.0实现方式:
1 SqlConnection conn = new SqlConnection("testconn"); 2 SqlCommand comm = conn.CreateCommand(); 3 string Sql = "SqlServerSpName"; 4 comm.CommandText = Sql; 5 comm.CommandType = CommandType.StoredProcedure; 6 comm.ExecuteNonQuery();
18.SQL Server的两种索引是何形式?索引的作用?索引的优缺点?
SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。
用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。一个表中只能存在一个聚集索引,因为一个表中的记录只能以一种物理顺序存放。由于聚集索引所以很大,所以要选择好建立聚集索引的列。(SQL Server默认将主键作为聚集索引有时并不符合设计要求)。
非聚簇索引需要大量的硬盘空间和内存。另外,虽然非聚簇索引可以提高从表中 取数据的速度,它也会降低向表中插入和更新数据的速度。每当你改变了一个建立了非聚簇索引的表中的数据时,必须同时更新索引。因此你对一个表建立非聚簇索引时要慎重考虑。如果你预计一个表需要频繁地更新数据,那么不要对它建立太多非聚簇索引。另外,如果硬盘和内存空间有限,也应该限制使用非聚簇索引的数量。
19.触发器的作用。
触发器(trigger)是个特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由个事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。
它们主要用于强制服从复杂的业务规则或要求。例如,您可以根据客户当前的帐户状态,控制是否允许插入新订单。触发器也可用于强制引用完整性,以便在多个表中添加、更新或删除行时,保留在这些表之间所定义的关系。然而,强制引用完整性的最好方法是在相关表中定义主键和外键约束。
20.Socket怎么实现?
1 IPAddress ipAddress = IPAddress.Parse("192.168.1.10"); 2 int port = 8080; 3 IPEndPoint remoteEP = new IPEndPoint(ipAddress, port); 4 // 生成一个TCP/IP socket. 5 Socket client = new Socket(AddressFamily.InterNetwork, 6 SocketType.Stream, ProtocolType.Tcp); 7 // 与目标终端连接. 8 client.Connect(remoteEP); 9 10 // 发送数据到远程终端. 11 // 格式转换. 12 byte[] byteData = Encoding.ASCII.GetBytes("Test"); 13 // 开始发送数据到远程设备. 14 client.Send(byteData, 0, byteData.Length, 0);