笔者近几年前前后后面试了50+公司左右,怎么讲呢,每个面试官的风格都不一样,要问的问题也不尽相同。但是面试是需要技巧的,提前准备工作以及如何把简历写得让人眼前一亮是很有必要的,关于这一块将在其它篇幅作介绍。话不多说,先总结出面试遇到的主流面试题,将分8篇文章,每篇25道,几乎涵盖90%以上的面试知识点,适用于中高级、架构师去复习,暂时还那么多时间把答案写出来,后续会加上,如果大家有好的答案也可以在评论区写出来,谢谢大家。
一、如何在.NET中做deep copy?
https://www.cnblogs.com/bincoding/p/8574145.html
二、throw与throw e的区别?应该用哪一个?
using System; using System.Text; namespace ConsoleApp4 { class Program { static void Main(string[] args) { try { Method2(); } catch (Exception ex) { Console.Write(ex.StackTrace.ToString()); Console.ReadKey(); } } private static void Method2() { try { Method1(); } catch (Exception ex) { throw; } } private static void Method1() { try { throw new Exception("Inside Method1"); } catch (Exception) { throw; } } } }
上面的代码输出的结果如下:显示了完整的层次结构和引发异常的方法名称。
现在单独把Method2()方法中的异常抛出throw改成throw ex。
private static void Method2() { try { Method1(); } catch (Exception ex) { throw ex; } }
接着再把程序运行一遍,执行的结果如下所示:可以看出引发异常的Method1并没有被抛出。throw ex忽略所有先前的层次结构,重置了堆栈跟踪,抛出的异常将成为“原始”异常,因此所有先前的堆栈跟踪都不会存在。
结论:引发异常后,堆栈携带的部分信息就是堆栈跟踪。堆栈跟踪是方法调用层次结构的列表,该列表以引发异常的方法开始并以捕获异常的方法结束。如果通过在throw语句中指定异常来重新引发异常(也就是使用throw ex),则堆栈跟踪将从当前方法重新启动,并且引发该异常的原始方法与当前方法之间的方法调用列表将丢失。要使原始堆栈跟踪信息与该异常保持一致,请使用throw语句而不指定该异常。
三、finally block是什么时候调用的?
先看代码如下:
static void Main(string[] args) { Console.WriteLine(GetNum()); } public static int GetNum() { int Num=1; try { Console.WriteLine("try"); return Num; } catch (Exception ex) { throw ex; } finally { ++Num; Console.WriteLine("finally"); } }
结论:try中的return语句先于finally中的函数执行所以,返回的结果是1, 而不是2。
从运行结果可以看出,return语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally语句后才真正开始返回。
四、out和ref有什么区别?
https://www.cnblogs.com/zoro-zero/p/13490144.html
五、在关系型数据库里,referential integrity(应用完整性)是什么意思?
六、解释this关键字?它可以在静态方法中使用吗?
https://www.cnblogs.com/zoro-zero/p/13490182.html
七、在GOF设计模式的3类中各选一个设计模式来说明其用途?
1)、单例模式
2)、工厂模式
3)、观察者模式
八、mock和stub有啥区别。
九、SoC是什么意思?
十、cross cutting concen(纵切关注)最好怎么处理?
十一、在EF中如何定义多对多关系?
public class Media // One entity table { public int Id { get; set; } public string Name { get; set; } public bool Enabled { get; set; } public virtual ICollection<ContractMedia> ContractMedias { get; set; } } public class Contract // Second entity table { public int Id { get; set; } public string Code { get; set } public virtual ICollection<ContractMedia> ContractMedias { get; set; } } public class ContractMedia // Association table implemented as entity { public int MediaId { get; set; } public int ContractId { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public double Price { get; set; } public virtual Media Media { get; set; } public virtual Contract Contract { get; set; } }
创建模型/实体后,需要在上下文中定义关系:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<ContractMedia>() .HasKey(c => new { c.MediaId, c.ContractId }); modelBuilder.Entity<Contract>() .HasMany(c => c.ContractMedias) .WithRequired() .HasForeignKey(c => c.ContractId); modelBuilder.Entity<Media>() .HasMany(c => c.ContractMedias) .WithRequired() .HasForeignKey(c => c.MediaId); }
十二、DateTime.Parse(myString) 这段代码有什么问题?应该怎么写?
应该使用TryParse,因为如果无法解析,Parse将会引发异常。
正确写法:
DateTime dt; DateTime.TryParse(myString,out dt);
十三、为什么catch(Exception)是个不好的写法?
catch(Exception):指定出现异常在catch块要处理,仅知道已引发异常,但是无法获取有关此异常的更多信息。
应该使用catch(Exception ex),catch(Exception ex) 传递引发的实际异常的实例,不但可以捕获异常并能获取异常的对象,可以检索出更多的异常相关的信息。
十四、.NET的垃圾回收是怎么管理CLR中的对象的生命周期的?
垃圾回收是.net中的运行时CLR这个库中的一个核心功能,目的就是为了提高内存的利用率。值类型这些变量,用完以后系统就立刻把这个内存销毁了。堆里面的内存如何被回收释放?不确定,一般都是当程序需要新内存,或者内存不够的时候开始执行回收。当然什么对象被回收什么对象不会被回收,垃圾回收机制它有选择,当这个对象没有用的时候,也就是没任何地方引用这个对象的时候就会被回收。
使用代的机制来回收,创建对象的时候,会分为3个代:第0代、第1代、第2代。创建对象的时候,每个代都有个初始大小,比如1M。每次创建新对象的时候都是向第0代开始创建,当第0代内存满后,就会执行垃圾回收机制,把没有任何引用的对象给回收销毁,然后把有引用的对象,也就是没被回收活下来的对象放到第1代。然后再次创建对象还是向第0代里面放,如此类推。如果说到了要回收第2代的时候,活下来的对象还是放到第二代,程序继续运行,直到这3代空间都满了,就会尝试把每一代的内存容量扩大。也不是一直扩充,后续扩充后内存还是满了就会抛异常了。
C/C++中由程序员进行对象的回收像学校食堂中由学生收盘子,.Net中由GC进行垃圾回收像餐馆中店员去回收。
GC是垃圾收集器(Garbage Collection)。程序员不用担心内存管理,因为垃圾收集器会自动进行管理。GC只能处理托管内存资源的释放,对于非托管资源则不能使用GC进行回收,必须由程序员手工回收,一个例子就是FileStream或者SqlConnection需要程序员调用Dispose进行资源的回收。
要请求垃圾收集,可以调用下面的方法:GC.Collect()一般不需要手动调用GC.Collect()。当一个对象没有任何变量指向(不再能使用)的时候就可以被回收了。
基础知识:当没有任何变量指向一个对象的时候对象就可以被回收掉了,但不一定会立即被回收。
object obj = new object();//只有new才会有新对象
Console.WriteLine(obj);
object obj2 = obj;
obj = null;
obj2 = null;
Console.WriteLine();
十五、Finalize()和Dispose()这2个方法有什么不同。
Finalize():析构函数的函数名跟构造函数一致,前面加了~符号,.net里面其实把析构函数叫终结函数,析构函数会被编译成Finalize函数。
作用:一般我们不用它,我们普通的托管资源垃圾会自动回收,但是假设我们类中访问了操作系统的资源,如非托管资源,垃圾是不会回收的。而在执行垃圾回收之前会先调用我们的析构函数进行非托管资源的释放然后再进行垃圾回收。
但是非托管资源,一般当对象不用的时候就应该要立马回收,而不是等待回收。所以多了dispose函数,一般类中有调用非托管资源,类就会继承IDisposable接口,然后实现Dispose方法,在Dispose中去释放非托管资源。在调用非托管资源的时候,再去调Dispose方法就会立马进行垃圾回收,所以一般我们不会去使用析构函数这个终结器。如果使用using的话,那就连Dispose方法都不需要手动调用了,会自动调用这个方法,执行垃圾回收。
十六、a.Equals(b) 和 a==b有什么不同?写出下列代码中Console打印出的结果。
static void Main(string[] args) { int a = 10; int b = 10; Console.WriteLine(a==b); Console.WriteLine(a.Equals(b)); StringBuilder sb1 = new StringBuilder("Asp.Net"); StringBuilder sb2 = new StringBuilder("Asp.Net"); Console.WriteLine(sb1 == sb2); Console.WriteLine(sb1.Equals(sb2)); }
解答:1、对于值类型:= = 和equals等价,都是比较存储信息的内容是否相等。
2、对于引用类型:= = 比较的是引用类型在栈中的地址,equals方法则比较的是引用类型在堆中的存储的内容是否相等。
十七、object identity(同一性)比较和object equality(相等性)比较有什么不同?
解答:相等性:两个对象,它们的值相等。
同一性:两个对象,它们的引用相等。
十八、请列出常见的缓存方式,并简要概述其优缺点。
十九、请使用linq语句表达式,查询出 int values={1,2,5,2,3,5,5,3,4,6,3,3};中出现次数最多的数字。
二十、WebService、WCF、WebApi有什么不同?
https://www.cnblogs.com/markli/p/4460564.html
二十一、可以采用foreach迭代的类的对象必须满足什么条件?
IEnumerable接口,GetEnumerator方法
二十二、列举C#依赖注入的方式,且相关优劣势说明。
https://www.cnblogs.com/zoro-zero/p/13490459.html
二十三、async标记的方法返回值有何要求?
https://www.cnblogs.com/zoro-zero/p/13490487.html
二十四、sleep()和wait()有什么区别?
https://www.cnblogs.com/zoro-zero/p/13490533.html
二十五、C#中Params是什么含义?有何用途?