当前位置:   article > 正文

2020年最新 C# .net 面试题,月薪20K+中高级/架构师必看(一)_net 6 cross cutting

net 6 cross cutting

笔者近几年前前后后面试了50+公司左右,怎么讲呢,每个面试官的风格都不一样,要问的问题也不尽相同。但是面试是需要技巧的,提前准备工作以及如何把简历写得让人眼前一亮是很有必要的,关于这一块将在其它篇幅作介绍。话不多说,先总结出面试遇到的主流面试题,将分8篇文章,每篇25道,几乎涵盖90%以上的面试知识点,适用于中高级、架构师去复习,暂时还那么多时间把答案写出来,后续会加上,如果大家有好的答案也可以在评论区写出来,谢谢大家。

一、如何在.NET中做deep copy?

1)使用序列化反序列化对象实现深度克隆

2)使用反射实现深度克隆

参考文章:C# 中的浅拷贝和深拷贝         如何在C#中做deep copy?

二、throw与throw e的区别?应该用哪一个?

先来看下面这个例子

  1. using System;
  2. using System.Text;
  3. namespace ConsoleApp4
  4. {
  5. class Program
  6. {
  7. static void Main(string[] args)
  8. {
  9. try
  10. {
  11. Method2();
  12. }
  13. catch (Exception ex)
  14. {
  15. Console.Write(ex.StackTrace.ToString());
  16. Console.ReadKey();
  17. }
  18. }
  19. private static void Method2()
  20. {
  21. try
  22. {
  23. Method1();
  24. }
  25. catch (Exception ex)
  26. {
  27. throw;
  28. }
  29. }
  30. private static void Method1()
  31. {
  32. try
  33. {
  34. throw new Exception("Inside Method1");
  35. }
  36. catch (Exception)
  37. {
  38. throw;
  39. }
  40. }
  41. }
  42. }

上面的代码输出的结果如下:显示了完整的层次结构和引发异常的方法名称。

现在单独把Method2()方法中的异常抛出throw改成throw ex。

  1. private static void Method2()
  2. {
  3. try
  4. {
  5. Method1();
  6. }
  7. catch (Exception ex)
  8. {
  9. throw ex;
  10. }
  11. }

接着再把程序运行一遍,执行的结果如下所示:可以看出引发异常的Method1并没有被抛出。throw ex忽略所有先前的层次结构,重置了堆栈跟踪,抛出的异常将成为“原始”异常,因此所有先前的堆栈跟踪都不会存在。

结论:引发异常后,堆栈携带的部分信息就是堆栈跟踪。堆栈跟踪是方法调用层次结构的列表,该列表以引发异常的方法开始并以捕获异常的方法结束。如果通过在throw语句中指定异常来重新引发异常(也就是使用throw ex),则堆栈跟踪将从当前方法重新启动,并且引发该异常的原始方法与当前方法之间的方法调用列表将丢失。要使原始堆栈跟踪信息与该异常保持一致,请使用throw语句而不指定该异常。

参考文章:“throw” 和“throw ex”之间有区别吗?

三、try中的return是否先于finally block调用?

先看代码如下:

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine(GetNum());
  4. }
  5. public static int GetNum()
  6. {
  7. int Num=1;
  8. try
  9. {
  10. Console.WriteLine("try");
  11. return Num;
  12. }
  13. catch (Exception ex)
  14. {
  15. throw ex;
  16. }
  17. finally
  18. {
  19. ++Num;
  20. Console.WriteLine("finally");
  21. }
  22. }

输出结果如下:

结论:try中的return语句先于finally中的函数执行所以,返回的结果是1, 而不是2。

从运行结果可以看出,return语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally语句后才真正开始返回。

参考文章:C#中的异常处理try catch finally

四、out和ref有什么区别?

相同点:两者都是按地址传递的,使用后都将改变原来参数的数值。

不同点:ref可以把参数的数值传递进函数,但是out是要把参数清空,就是说你无法把一个数值从out传递进去的,out进去后,参数的数值为空,所以你必须初始化一次。这个就是两个的区别,或者说就像有的网友说的,ref是有进有出,out是只出不进。

参考文章:C#关键字out与ref的区别

五、在关系型数据库里,referential integrity(参照完整性)是什么意思?

参照完整性是对关系数据库中建立关联关系的数据表间数据参照引用的约束,也就是对外键的约束。是指关系中的外键必须是另一个关系的主键有效值,或者是NULL。参照完整性规则规定外键不能引用不存在的实体。

参考文章:关系型数据库中,关系的完整性有哪几种

六、解释this关键字?它可以在静态方法中使用吗?

C#中的this主要有4种用途,分别是:(1)代表当前类的实例对象;(2)代表当前类的无参构造函数;(3)为原始类型定义扩展方法,将对象作为参数传递给方法;(4)申明索引器。

this不可用于静态方法中。原因是,this访问的是类的实例,也就是对象,而静态成员只能由类来访问,不能由对象来访问。this关键字只能在实例构造函数、实例方法或实例访问器中使用。

参考文章:C#中this关键字的用法详解

七、在GOF设计模式的3类中各选一个设计模式来说明其用途?

八、mock和stub有啥区别。

九、SoC是什么意思?

十、cross cutting concen(纵切关注)最好怎么处理?

十一、在EF中如何定义多对多关系?

  1. public class Media // One entity table
  2. {
  3. public int Id { get; set; }
  4. public string Name { get; set; }
  5. public bool Enabled { get; set; }
  6. public virtual ICollection<ContractMedia> ContractMedias { get; set; }
  7. }
  8. public class Contract // Second entity table
  9. {
  10. public int Id { get; set; }
  11. public string Code { get; set }
  12. public virtual ICollection<ContractMedia> ContractMedias { get; set; }
  13. }
  14. public class ContractMedia // Association table implemented as entity
  15. {
  16. public int MediaId { get; set; }
  17. public int ContractId { get; set; }
  18. public DateTime StartDate { get; set; }
  19. public DateTime EndDate { get; set; }
  20. public double Price { get; set; }
  21. public virtual Media Media { get; set; }
  22. public virtual Contract Contract { get; set; }
  23. }

创建模型/实体后,需要在上下文中定义关系:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<ContractMedia>()
  4. .HasKey(c => new { c.MediaId, c.ContractId });
  5. modelBuilder.Entity<Contract>()
  6. .HasMany(c => c.ContractMedias)
  7. .WithRequired()
  8. .HasForeignKey(c => c.ContractId);
  9. modelBuilder.Entity<Media>()
  10. .HasMany(c => c.ContractMedias)
  11. .WithRequired()
  12. .HasForeignKey(c => c.MediaId);
  13. }

十二、DateTime.Parse(myString) 这段代码有什么问题?应该怎么写?

应该使用TryParse,因为如果无法解析,Parse将会引发异常。
正确写法:

  1. DateTime dt;
  2. 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打印出的结果。

  1. static void Main(string[] args)
  2. {
  3. int a = 10;
  4. int b = 10;
  5. Console.WriteLine(a==b);
  6. Console.WriteLine(a.Equals(b));
  7. StringBuilder sb1 = new StringBuilder("Asp.Net");
  8. StringBuilder sb2 = new StringBuilder("Asp.Net");
  9. Console.WriteLine(sb1 == sb2);
  10. Console.WriteLine(sb1.Equals(sb2));
  11. }

解答: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有什么不同?

二十一、可以采用foreach迭代的类的对象必须满足什么条件?

二十二、列举C#依赖注入的方式,且相关优劣势说明。

二十三、async标记的方法返回值有何要求?

二十四、sleep()和wait()有什么区别?

二十五、C#中Params是什么含义?有何用途?

1)用parmas修饰的可变参数数组必须是一维数组。

2)不允许将params修饰符与ref和out修饰符组合起来使用 ,但是可以分开使用,也就是params前一个参数可以定义为ref或者out。 

3)传递到形参params中的数据可以是跟params类型一致的数组,也可以是任意多个与该params类型一致的变量。还可以不发送参数。 如果未发送任何参数,则 params 列表的长度为零。

4)params参数必须是参数表的最后一个参数,并且在方法声明中只允许一个 params 关键字。

5)若实参是数组则按引用传递,若实参是变量或表达式则按值传递。

参考文章:C#中可变参数params解析

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/114870
推荐阅读
  

闽ICP备14008679号