《C#面试题精选(共15页).doc》由会员分享,可在线阅读,更多相关《C#面试题精选(共15页).doc(15页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、精选优质文档-倾情为你奉上一、 接口与抽象类 抽象类:1)抽象类是一种不能直接实例化而必须从中继承的类,抽象类实例由它的子类给出,抽象类可以提供实现,也可以不提供实现,2)抽象类可以定义非静态的类变量,抽象类成员可以是私有的,受保护的,公有的3)抽象类是单继承(子类只能继承一个抽象类);4)抽象类主要用于关系密切的对象,若设计大的功能单元,则使用抽象类; 抽象类用于共性;5)在抽象类中加入一个方法,那么它的子类就同时有了这种方法; 接口:1)接口不提供任何实现,接口的实例是实现接口的类给出的2)接口成员不能包含常数,字段,运算符,实例构造函数,析构函数,静态成员等,只能是方法,属性,事件,或索
2、引器,且其成员都是共有的3)多继承,多态性,类或结构可以继承多个接口;4)适合于为不相关的类提供通用功能;接口用于规范5)在接口中加入方法,那实现它的类就必须重新编写; 二、 装箱和拆箱装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作。1. 装箱在值类型向引用类型转换时发生: object obj = 1;2.拆箱在引用类型向值类型转换时发生装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以在c# 2.0之后引入了泛型来减少装箱操作和拆箱操作消耗。三、 return,break,continue1、return 语句的作用从当前的方法中退出,返回到该调用的方法的语句处,继续执行。 2
3、、break语句的作用 用在循环体内和switch语句体内;用在循环体内表示跳出本层循环体,从而提前结束本层循环;用在switch中表示跳出跳出switch语句体。3、continue语句作用 其作用是结束本次循环,即跳过本次循环体中余下尚未执行的语句,接着再一次进行循环的条件判定。 四、 用.net做B/S结构的系统,您是用几层结构来开发,每一层之间的关系目前.Net平台下的B/S开发框架基本可以分为三大类:基于控件和页面事件驱动思想的Web Forms;基于模型、视图、控制器的MVC模式;综合了Web Forms和MVC的一些特点而产生的框架;表现层(UI)、业务逻辑层(BLL)、数据访问
4、层(DAL)表现层负责与用户交互;业务逻辑层就是指派数据访问层进行表现层请求的逻辑性的业务处理,数据访问层对数据库进行增删查改。优点: 分工明确,条理清晰,易于调试,而且具有可扩展性。缺点: 增加成本。五、 简述数据库连接池连接池基本的思想是在的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、
5、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等连接池为每个唯一的连接字符串创建;连接池一旦创建,直到活动进程终止时才会被毁坏六、 C# (Winform)利用委托跨线程界面交互问题 窗体上的控件只允许创建它们的线程访问,也就是主线程,若非主线程访问则会发生异常,可借助于控件的InvokeRequired属性判断该控件目前是否被主线程访问,如果是则返回false;在利用Invoke方法找到主线程(在哪个控件上调用了Invoke,就用那个控件所在的线程处理委托方法,Invoke的两个参数分别是:委托、委托的方法需要的参数),让主线程访问控件的方法。(1) 创建一个类
6、,类里声明委托及委托对象,在类的方法中调用委托对象classTestClass/声明一个delegate(委托):testDelegate,该类型可以搭载返回值为空,参数只有一个 (long型)的方法。 publicdelegatevoidtestDelegate(longi); /声明一个testDelegate类型的对象。它可以搭载N个方法。 publictestDelegatemainThread; publicvoidtestFunction() longi=0; while(true) i+; mainThread(i); /调用委托对象 Thread.Sleep(1000);/线程
7、等待1000毫秒 (2) 在UI控件的事件中创建TestClass类并为委托搭载方法,创建无参线程privatevoidbutton1_Click(objectsender,EventArgse)/创建TestClass类的对象TestClasstestclass=newTestClass();/在testclass对象的mainThread(委托)对象上搭载两个方法,在线程中调用mainThread对象时相当于调用了这两个方法。testclass.mainThread=newTestClass.testDelegate(refreshLabMessage1);testclass.mainTh
8、read+=newTestClass.testDelegate(refreshLabMessage2);/创建一个无参数的线程,这个线程执行TestClass类中的testFunction方法。ThreadtestclassThread=newThread(newThreadStart(testclass.testFunction);/启动线程,启动之后线程才开始执行testclassThread.Start();(3) 在搭载的方法中判断该方法是否被主线程调用,若非主线程调用则再创建TestClass并为委托对象搭载方法,/在界面上更新线程执行次数privatevoidrefreshLabM
9、essage1(longi)/判断该方法是否被主线程调用,也就是创建labMessage1控件的线程,当控件的InvokeRequired属性为ture时,说明是被主线程以外的线程调用。如果不加判断,会造成异常if(this.labMessage1.InvokeRequired)/再次创建一个TestClass类的对象TestClasstestclass=newTestClass();/为新对象的mainThread对象搭载方法testclass.mainThread=newTestClass.testDelegate(refreshLabMessage1);/this指窗体,在这调用窗体的I
10、nvoke方法,也就是用窗体的创建线程来执行mainThread对象委托的方法,再加上需要的参数(i)this.Invoke(testclass.mainThread,newobjecti);elselabMessage1.Text=i.ToString();七、 C#动态链接库(Dynamic Link Library)使用方法Dll不能直接执行,需要根据进程的需要按需载入,DLL只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该DLL也只能被该进程的线程访问,它的句柄可以被调用进程使用,而调用进程的句柄也可以被该Dll使用。非托管代码主要是基于win 32平台
11、开发的DLL,activeX的组件,托管代码是基于.net平台开发的。1、 生成动态链接库(以类库为例)新建一个类库(DllTest)-解决方案(右击属性)-配置程序集名称(DllTest),默认命名空间(DllTest),输出类型(类库) -菜单栏“生成”选项-选择生成“DllTest” -在解决方案目录下的DllTestDllTestbinDebug文件夹下生成DllTest.dll2、 C#调用DLL中的非托管函数(这是个全局函数)(1) 准备1) 在项目解决方案下的“引用”文件夹下“添加引用”,将待引用的动态链接库引用到项目解决方案。2) 在需要引用动态库的.cs文件中添加引用(usi
12、ng System.Runtime.InteropServices;)3) 待调用的Dll文件必须位于程序当前目录或系统定义的查询路径中(2) 调用DLL中的函数或方法1) 调用DLL中的非托管函数首先,在C#语言源程序中声明外部方法,其基本形式是:DLLImport(“DLL文件”)修饰符 extern 返回变量类型 方法名称 (参数列表)示例: DllImport(kernel32.dll,EntryPoint= GetProcAddress ,CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall,ExactSp
13、elling=false) private extern static IntPtr GetAddress(IntPtr lib, string funcName);注:DLLImport第一个参数为DLL文件路径;第二个参数为待调用的DLL函数名;第三个参数为用在入口点中的字符集;第四个参数指示入口点的调用约束;第五个参数指示EntryPoint是否必须与指示的入口点的拼音完全匹配。在方法的声明中若需要使用其他方法名称,需要在DLLImport中设置EntryPoint参数,上例中的GetAddress就是对GetProcAddress方法的更名。3、 C#动态装载、调用DLL中的非托管函数
14、用DllImport调用DLL中的非托管函数,但是这个是全局的函数,假若DLL中的非托管函数有一个静态变量S,每次调用这个函数的时候,静态变量S就自动加1。结果,当需要重新计数时,就不能得出想要的结果。因为C#中使用DllImport是不能像动态load/unload assembly那样,所以只能借助API函数了。在kernel32.dll中,与动态库调用有关的函数包括3:LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。FreeLibrary(或MFC的AfxFreeLibr
15、ary),释放动态链接库。(1)建一个类库using System.Runtime.InteropServices; /用DllImport需用此命名空间using System.Reflection; /使用IAssembly需要此命名空间using System.Reflection.Emit; /使用ILGenerator需要此命名空间class DllInvoke /声明LoadLibrary,参数path为DLL文件全路径,该函数返回函数库模块的句柄 DllImport(kernel32.dll) private extern static IntPtr LoadLibrary(st
16、ring path); /声明GetProcAddress,参数lib为包含需调用函数的函数库模块的句柄;funcName为调用函数的名称,该函数返回函数句柄 DllImport(kernel32.dll) private extern static IntPtr GetProcAddress(IntPtr lib, string funcName); /声明FreeLibrary,参数lib为需释放的函数库模块的句柄,该函数返回是否已释放指定的 Dll DllImport(kernel32.dll) private extern static bool FreeLibrary(IntPtr
17、lib); private IntPtr hLib; /存储函数库模块的句柄 public DllInvoke(String DLLPath) /构造函数,获取函数库模块句柄 hLib = LoadLibrary(DLLPath); / 若函数库模块的句柄为空,则抛出异常 if (hLib = IntPtr.Zero) throw (new Exception( 没有找到 :+DLLPath); DllInvoke() /析构函数,释放动态链接库 FreeLibrary(hLib); hLib = IntPtr.Zero; /取得函数指针,将要执行的函数转换为委托 public Delegat
18、e Invoke (string APIName,Type t) IntPtr api = GetProcAddress(hLib, APIName); / 若函数指针,则抛出异常 if (api = IntPtr.Zero) throw(new Exception(没有找到:+ APIName + 这个函数的入口点); return (Delegate)Marshal.GetDelegateForFunctionPointer(api, t); (2)在主函数中调用动态链接库,该示例实现控制台程序运行时自动全屏class Program DllImport(kernel32.dll) pub
19、lic static extern IntPtr GetStdHandle(int nStdHandle); const int STD_OUTPUT_HANDLE = -11; public delegate bool SetConsoleDisplayMode(IntPtr hOut, int dwNewMode, out int lpdwOldMode); static void Main(string args) DllInvoke dll = new DllInvoke(kernel32.dll); int dwOldMode; /标准输出句柄 IntPtr hOut = GetSt
20、dHandle(STD_OUTPUT_HANDLE); /调用Win API,设置屏幕最大化 SetConsoleDisplayMode s = (SetConsoleDisplayMode)dll.Invoke(SetConsoleDisplayMode, typeof(SetConsoleDisplayMode); s(hOut, 1, out dwOldMode); Console.WriteLine(*Full Screen Mode*); Console.ReadLine(); 八、 C#反射(Reflection)4、 什么是反射(1) 反射的定义通过System.Reflecti
21、on命名空间的类以及System.Type,可以在运行时获取.NET中每一个类型(如类、结构、接口、委托和枚举等)的信息,还可以获得每个成员的名称、限定符和参数等。可以使用反射动态地创建类型的实例,或从现有对象中获取类型,或获取类中的私有字段及其值。(2)反射应用的基本类型.net所编写的程序集包含两个重要部分:IL(代码) 和metadata(, 根据代码具体内容形成类的记录信息),;应用程序结构分为应用程序域、程序集、模块、类型、成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。反射提供了封装程序集、
22、模块和类型的对象,反射即是根据元数据的记录找到所需的代码:1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。4)使用MethodInfo了解方法的名称、返
23、回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。8)使用ParameterInfo
24、了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。9)System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。10)反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息。11)此外,Jscript等语言编译器使用反射来构造符号表。System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段,System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。12)System.Type类可以获得对象的类型信息,此
25、信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。(3)在设计模式实现中使用反射技术 采用反射技术可以简化工厂的实现。1):通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。2):使用反射可以减少抽象工厂的子类。采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。3)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。4)享元模式:采用反射技术实例化享元可以简化享元工厂
26、。(4)优缺点 优点:反射提高了程序的灵活性和扩展性;降低耦合性,提高自适应能力;它允许程序创建和控制任何类的对象,无需提前硬编码目标类 缺点:有些元数据信息是不能通过反射获取的;使用反射动态绑定需要牺牲性能(clr 需要做更多的工作:校验参数,检查权限等,所以速度要远慢于直接代码);使用反射会带来维护的问题,反射代码比相应的直接代码更复杂所以尽量不要使用反射进行编程,对于打算编写一个动态构造类型(晚绑定)的应用程序,可以采取以下的几种方式进行代替: 1)通过类的继承关系。让该类型从一个编译时可知的基础类型派生出来,在运行时生成该类型的一个实例,将对其的引用放到其基础类型的一个变量中,然后调用
27、该基础类型的虚方法。 2)通过接口实现。在运行时,构建该类型的一个实例,将对其的引用放到其接口类型的一个变量中,然后调用该接口定义的虚方法。 3)通过委托实现。让该类型实现一个方法,其名称和原型都与一个在编译时就已知的委托相符。在运行时先构造该类型的实例,然后在用该方法的对象及名称构造出该委托的实例,接着通过委托调用你想要的方法。这个方法相对与前面两个方法所作的工作要多一些,效率更低一些。5、 命名空间与装配件的关系装配件是.NET应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。装配件和命名空间的关系不是一一对应的,也不互相包含,一个装配件里面可以有多个命名空间,一个命名空间也
28、可以在多个装配件中存在;命名空间说明类型是哪个族的,比如汉族、回族;装配件是类型居住的地方,在程序中使用一个类,就必须高速编译器这个类住在哪,也就是说必须引用该装配件。若在编写程序时,不确定这个类在哪里,仅仅知道它的名称,可以使用反射(在程序运行的时候提供该类型的地址)找到它。6、 如何使用反射(示例)(1) 反射程序集1) 反射AppDomain中包含的所有程序集Appdomain.currentDomain.GetAssemblies()2) 利用反射获取装配件(dll,exe)信息Assembly dll = Assembly.LoadFile(Environment.CurrentDi
29、rectory + Test.dll”);Assembly exe = Assembly.LoadFrom(“D:reflecting.exe”)Assembly dll =Assembly.LoadFrom(Server.MapPath(bin/swordyang.dll)3) 利用反射获取类型所在程序集DataTabledt=newDataTable(); AssemblyotherAssembly=dt.GetType().Assembly;Assembly assembly = Assembly.GetExecutingAssembly(); / 获取当前程序集 4)创建类的实例obj
30、ect obj = assembly.CreateInstance(类的完全限定名(即包括命名空间)); (2) 获取类型信息1)通过Assembly.GetType()方法获取类型信息Typemath=dll.GetType(TestDll.Math,true); 2) 通过Type.GetType()方法获取类型信息 Type t = Type.GetType(“System.String”);3)通过调用System.Object的方法GetType来获取实例对象的类型对象public void Process( object processObj )Type t = processsO
31、bj.GetType();if( t.GetInterface(“ITest”) !=null ) /判断该类型是否实现了ITest接口(3) 根据类型动态创建对象(using System.Activator;) 1) 根据类型动态创建无参对象Type t = Type.GetType(System.Data.DataTable,System.Data,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5ce089);DataTable table = (DataTable)Activator.CreateInstance(t);
32、 2) 根据类型动态创建有参对象Type t = Type.GetType(“TestSpace.TestClass”);/构造器参数,将参数按顺序放入Object数组中Object constructParms = new object “hello”; TestClass obj = (TestClass)Activator.CreateInstance(t,constructParms);(4)获取方法以及动态调用方法(TestClass有一个GetValue方法,一个有参构造器,一个Value属性)1)获取方法的信息MethodInfo method = t.GetMethods();
33、 /获取t类型下的所有方法信息MethodInfo method = t.GetMethod(GetValue); /获取单个方法信息/调用方法的一些标志位,这里的含义是Public,实例方法、该类中成员不包含父类成员BindingFlagsflag=BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly;/GetValue方法的参数object parameters = new objectHello;/调用方法,用一个object接收返回值object returnValue = method.Invoke(o
34、bj,flag,Type.DefaultBinder,parameters,null);(5)动态创建委托(using System.Delegate;)delegate string TestDelegate(string value);TestClass obj = new TestClass();/创建代理,传入类型、创建代理的对象以及方法名称TestDelegate method = (TestDelegate)Delegate.CreateDelegate(typeof(TestDelegate),obj,”GetValue”);String returnValue = method
35、(“hello”);九、 多线程1、 使用线程的理由1)可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。2)可以使用线程来简化编码。3)可以使用线程来实现并发执行。2、 基本知识1)进程与线程:进程是操作系统执行应用程序的基本单位,拥有应用程序的资源;线程是进程中的基本执行单位,进程的资源被线程共享,线程不拥有资源。2)前台线程和后台线程:通过Thread类新建线程默认为前台线程。当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常。可通过Thread实例的IsBackground属性设置线程为后台线程3)挂起(Suspend)和唤醒(Resume):由于线程的执行顺序和程
36、序的执行情况不可预知,所以使用挂起和唤醒容易发生死锁的情况,在实际应用中应该尽量少用。4)阻塞线程:通过使用Thread实例的Join()方法阻塞主线程,直到后台线程执行完才运行主线程。Thread backthread = new Thread(Worker); backthread.IsBackground = true;backthread.Join();5)终止线程:Abort:抛出 ThreadAbortException 异常让线程终止,终止后的线程不可唤醒,异常只会在调用Abort方法的线程中发生,而不会在主线程中抛出,并且调用Abort方法后线程的状态不是立即改变为Aborte
37、d状态,而是从AbortRequested-Aborted。Interrupt:抛出 ThreadInterruptException 异常让线程终止,通过捕获异常可以继续执行,异常只会在调用Interrupt方法的线程中发生,而不会在主线程中抛出,并且调用Interrupt方法后线程的状态不是立即改变为Stopped状态,而是从Running-Stopped6)线程优先级:Lowest、BelowNormal 、AboveNormal、Normal、 Highest,默认为Normal。通过设置Thread的Priority属性来改变线程的优先级,它的类型为ThreadPriority枚举类
38、型3、 线程的使用线程函数通过委托传递,可以不带参数,也可以带参数(只能有一个参数),可以用一个类或结构体封装参数。namespace Test class Program static void Main(string args) Thread t1 = new Thread(new ThreadStart(TestMethod); Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod); t1.IsBackground = true; t2.IsBackground = true; t1.Start(); t2.Sta
39、rt(hello); Console.ReadKey(); public static void TestMethod() Console.WriteLine(不带参数的线程函数); public static void TestMethod(object data) string datastr = data as string; Console.WriteLine(带参数的线程函数,参数为:0, datastr); 4、 线程池由于线程的创建和销毁需要耗费一定的开销,过多的使用线程会造成内存资源的浪费,出于对性能的考虑,于是引入了线程池的概念。线程池维护了一个操作请求队列,线程池的代码从队
40、列提取任务,然后委派给线程池的一个线程执行,线程执行完不会被立即销毁,这样既可以在后台执行任务,又可以减少线程创建和销毁所带来的开销。(通过线程池创建的线程默认为后台线程,优先级默认为Normal。1)创建工作者线程(向线程池的队列添加一个工作项以及一个可选的状态数据)public static bool QueueUserWorkItem (WaitCallback callBack);public static bool QueueUserWorkItem(WaitCallback callback, Object state);/工作项就是由callback参数标识的一个方法,该方法将由
41、线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型(带一个参数,无返回值),定义为:public delegate void WaitCallback(Object state)namespace Test class Program static void Main(string args) int worknum,ionum;/ 设置线程池中工作者线程数量为1000,I/O线程数量为1000 ThreadPool.SetMaxThreads(1000, 1000);/ 获得线程池中可用的线程ThreadPool.GetAvailabl
42、eThreads(out worknum, out ionum); /将工作项加入到线程池队列中,这里可以传递一个线程参数 ThreadPool.QueueUserWorkItem(TestMethod, Hello); Console.ReadKey(); public static void TestMethod(object data) string datastr = data as string; Console.WriteLine(datastr); 2) 协作式取消(取消操作) /在主线程下创建System.Threading.CancellationTokenSource对象 CancellationTokenSource cts = new CancellationTokenSource(); /将callback任务添加到线程池,任务参数为CancellationToken ThreadPool.QueueUserWorkItem(callback,cts.Token); Cts.Cancel(); /取消callback所