net高级程序员面试题-c#基础

c#基础:
一、请介绍关键字internal、sealed、readonly、static、is、as、params、this。请结合实际场景说明你在项目中的使用状况。
(1)    internal 访问修饰符 只有在同一程序集的文件中,内部类型或成员才是可访问的,内部访问一般用于基于组件的开发,由于它使一组组件可以以私有方式进行合做,而没必要向应用程序代码的其他部分公开。
(2)   sealed 修饰符能够应用于类、实例方法和属性。密封类不能被继承。密封方法会重写基类中的方法,但其自己不能在任何派生类中进一步重写。当应用于方法或属性时,sealed 修饰符必须始终与 override一块儿使用
(3)   readonly 字段能够在声明或构造函数中初始化。所以,根据所使用的构造函数,readonly 字段可能具备不一样的值。const 字段是编译时常数,常数表达式是在编译时被彻底计算出来,它只能在该字段的声明中初始化. const 默认就是静态的,而 readonly 若是设置成静态的就必须显示声明。
(4)  static 修饰符声明属于类型自己而不是属于特定对象的静态成员,尽管类的实例包含该类全部实例字段的单独副本,但每一个静态字段只有一个副本。不可使用  this 来引用静态方法或属性访问器。若是对类应用 static 关键字,则该类的全部成员都必须是静态的。
(5)  abstract 修饰符指示所修饰的内容缺乏实现或未彻底实现。 abstract 修饰符可用于类、方法、属性、索引器和事件。 标记该类为抽象类,抽象方法只能由派生类实现,是一个隐式的虚方法。
(6)  is 检查对象是否与给定类型兼容。 例如,if (obj is MyObject) 的代码能够肯定对象是否为 MyObject 类型的一个实例,或者对象是否为从 MyObject 派生的一个类型。请注意,is 运算符只考虑引用转换、装箱转换和取消装箱转换。 不考虑其余转换,如用户定义的转换。在 is 运算符的左侧不容许使用匿名方法。 lambda 表达式属于例外
(7)  as用于在兼容的引用类型之间执行转换,as 运算符相似于强制转换操做;可是,若是转换不可行,as 会返回 null 而不是引起异常。更严格地说,这种形式的表达式 就至关于 obj as type===>obj is type ? (type)obj : (type)null
as 运算符只执行引用转换和装箱转换。as 运算符没法执行其余转换,如用户定义的转换,这类转换应使用 cast 表达式来执行。
(8)   this 关键字引用类的当前实例(对象),还可用做扩展方法的第一个参数的修饰符。因为静态成员函数存在于类一级,而且不是对象的一部分,所以没有 this 指针。 在静态方法中引用 this 是错误的。
this能够限定被类似的名称隐藏的成员,例如当前实例有变量name,有一个方法的参数也是name,那么此时this.name就是指当前实例的成员,而不是只参数,起到一个区分的做用。This也能够将对象做为参数传递到其余方法
(9)   params 关键字能够指定在参数数目可变处采用参数的方法参数。在方法声明中的 params 关键字以后不容许任何其余参数,而且在方法声明中只容许一个 params 关键字。public static void UseParams2(params object[] list){}
2 ref 和out
关键字使参数按引用传递(参数默认是按照值传递的)。其效果是,当控制权传递回调用方法时,在方法中对参数所作的任何更改都将反映在该变量中。若要使用 ref或out参数,则方法定义和调用方法都必须显式使用 ref 和out关键字。传递到 ref 参数的参数必须最早初始化。这与 out 不一样,out 的参数在传递以前不须要显式初始化
3 delegate委托 ,事件
是一种可用于封装命名或匿名方法的引用类型,经过将委托与命名方法或匿名方法关联,能够实例化委托。 必须使用具备兼容返回类型和输入参数的方法或 lambda 表达式实例化委托
 delegate void testDelegateMes 声明了一个委托 能够理解为一个特殊的类
             testDelegateMes tdm1=new testDelegateMes(showMes);
           testDelegateMes tdm1 //能够理解为声明了该类的一个变量
           new testDelegateMes(showMes);//将委托与命名方法或匿名方法关联,此时tdm1就等价于showmes方法,一个委托能够搭载多个方法
  委托是一个类,它定义了方法的类型,使得能够将方法看成另外一个方法的参数来进行传递,这种将方法动态地赋给参数的作法,能够避免在程序中大量使用If-Else(Switch)语句,同时使得程序具备更好的可扩展性。全部委托都有一个构造器默认获取两个参数,一个是对象引用@object,一个是引用了回调方法的整数method;在构造器内部这两个参数被保存在_target和_methodPtr私有字段中,编译器在定义委托类的时候定义了一个Invoke方法,在Invoke被调用时(默认使用委托时,实际上是调用了该委托的Invoke方法),它使用私有字段_target和_methodPtr在指定对象上调用包装好的回调方法(委托代替方法执行的原理)。(invoke方法的签名和委托的签名匹配)
委托的属性表现形式就是事件,利用委托的多播特性能够将多个行为与委托绑定。
4 请说一下静态构造函数和默认构造函数的区别
               静态构造函数同其余静态成员同样是属于类的,它用来初始化一些静态成员。静态构造函数只会被执行一次。也就是在建立第一个实例或引用任何静态成员以前,由.NET自动调用。也就是说咱们没法直接调用静态构造函数。若是没有写静态构造函数,而类中包含带有初始值设定的静态成员,那么编译器会自动生成默认的静态构造函数。一个类只能有一个静态构造函数。
5 请说一下属性和字段的区别。
       属性有比字段更大的灵活性和可扩展性,字段”是直接在类或结构中声明的任何类型的变量。属性提供灵活的机制来读取、编写或计算某个私有字段的值。 能够像使用公共数据成员同样使用属性,但实际上它们是称做“访问器”的特殊方法。有助于提升方法的安全性和灵活性。对于属性,你能够自定义取值赋值的操做。能够只读也能够只写,而字段始终是可读可写的(字段通常属于类私有的,在外部访问他破坏了类的封装型)
6 什么是堆,栈, 值类型, 引用类型 ,装修, 拆箱
托管堆和线程栈是内存上的两块存储区域,应用程序的每一个线程都会被分配1m的存储空间做为线程栈,用于存储自身的数据(局部变量,实参等)这块内存区域的分配和回收不受应用程序干扰,当一个变量离开其做用域的时候内存空间当即被释放。栈在内存中是由高内存地址往低内存地址填充,释放是由低往高释放(先进后出保证了不与变量的声明周期起冲突)。栈中存储值类型
另外一块内存区域称为“堆(heap)”,在.NET 这种托管环境下,堆由CLR 进行管理,因此又称为“托管堆(managed heap)”。  托管堆存储引用类型,类是引用类型,用new关键词建立的对象分配的内存空间位于托管堆上。当对象使用完被垃圾回收机制回收时,空间会被回收   与栈不一样,堆是从下往上分配,因此自由的空间都在已用空间的上面
通用类型系统(CTS)区分两种基本类型:值类型和引用类型。它们之间的根本区别在于它们在内存中的存储方式。值类型存储于栈中,引用类型存储在堆中。值类型比引用类型轻。缘由是它不做为对象在托管堆中分配,不被垃圾回收,也不经过指针进引用。没有堆上每一个对象成员都有的额外成员:(类型对象指针和同步块索引),当这个值类型离开做用域时,所占空间即被释放
string是特殊的引用类型,字符串表明一个固定不变的Unicode字符序列(相似于不变集合同样,每当改变都不是针对原来的对象的改变,而是产生的一个新的对象)。这种不变性意味着,一旦在堆中分配了一个字符串,它的值将永远不会改变。若是值改变了,.NET就建立一个全新的String对象,并把它赋值给该变量。
装箱:
值类型转化为引用类型要使用装箱机制,装箱分三步:
  1.  在托管堆中分配内存,分配的内存量是值类型各字段所需的内存量,还要加上托管堆上全部对象都有的两个成员(类型对象指针和同步块索引)
  2. 值类型的字段复制到新分配的堆内存
  3. 返回对象地址,如今该地址是对象引用。值类型成了引用类型。
拆箱:
将一个引用类型转化为值类型的时候,要拆箱。值类型转为引用类型分2步:
  1.  获取已装箱的对象(此时为引用类型)的各个成员的地址;叫拆箱
  2.  将字段包含的值从堆中复制到基于栈的值类型实例中
拆箱不是将装箱过程倒过来,拆箱的代价要比装箱低不少,拆箱就是获取指针的过程,该指针指向一个对象中的原始值类型(数据字段).紧接着拆箱每每会发生一次字段复制
发生装箱和拆箱的几种状况:
1 发生值类型转为引用类型的状况 会装箱
2 类型在调用tostring方法时,内部并未重写该虚方法,而是使用的基类(object)得tostring方法,会装箱,在重写了基类tostring方法的时候,是用base.tostring同样会发生装箱
3 是用gettype 同上,在值类型的派生类未重写基类(object)得方法时同样会发生装箱
4 将值类型的某个实例转换为类型的某个接口时,要对实例进行装箱。这是由于接口变量必须包含对堆对象的引用
7 CLR垃圾回收机制
clr要求全部对象都从托管堆分配,当使用new操做符建立一个对象的时候会在堆上分配一块空间,并返回指向这块存储区域的指针(引用地址),当托管堆没有足够的空间的时候会执行垃圾回收。在执行垃圾回收时,
1 clr会暂停全部线程,
2 并标记全部对象是不是可达的(是否还有根(变量)引用它),不可达对象将被执行回收
3 以后进入压缩阶段,将全部可达的对象执行压缩,使他们占用连续的堆内存空间(好处是:1全部对象内存中紧挨在一块儿,恢复了引用的局部化,减少了应用程序的工做集,提高了性能。2可用空间是连续的,容许其余对象入住。3压缩意味着解决了原生堆空间碎片化问题)。同时,clr还要从每一个根减去所引用的对象在内存中偏移的字节数,这样就能保证每一个根仍是引用和以前同样的对象。
4 clr恢复应用程序全部线程
简单来讲整个过程分五步:暂停进程的全部线程——>将全部对象的同步块索引位设置为0——>检查全部根引用的对象,并标记其同步块索引位1,没有被根引用的对象保持原来的0不被从新标记——>删除标记为0的对象,执行压缩,并根据对象在内存中偏移的字节数调整根的引用地址——>clr恢复暂停的线程
clr的GC是基于代的垃圾回收器
clr的代一共分为3代:0,1,2;为每一代都分配了预算容量(KB),这个预算容量是根据回收的状况进行动态调整的,若是一次对第0代垃圾回收的对象较少,说明可用对象较多,则会增大对第一代预算,相反则相应减小容量预算。
对象被建立时未第0代对象,当0代存储空间满后,执行一次gc后幸存的对象升级为第1代对象,以后在0代空间执行第2次gc后幸存的对象继续升级为第一代对象,直到第1代存储空间也满了,在执行gc时,会同时回收第0代和第一代的对象(此时1代空间的对象极可能已经有不可达对象等待被回收),这时原来存储在第1代空间的幸存对象会被提高未第2代。第0代幸存的提高为第1代。当全部存储空间都满了的时候,就内存溢出了。
垃圾回收的触发条件:
  1. clr在检测第0代超过预算时
  2. 代码显示调用system.GC的静态方法Collect,强制垃圾回收
  3. window报告低内存时
  4. clr正在卸载AppDomain时,clr认为一切都不是根,执行涵盖全部代码的垃圾回收
  5. 进程结束时,clr正常关闭,windows将回收进程的全部内存
 
8 深拷贝和浅拷贝
深拷贝和浅拷贝之间的区别在因而否复制了子对象。浅拷贝(影子克隆/shallow copy):只复制对象的值类型字段,对象的引用类型,仍属于原来的引用. 深拷贝(深度克隆):不只复制对象的值类型字段,同时也复制原对象中的对象.就是说彻底是新对象产生的。例如一个学校类里不只包含了schoolname属性,还包含了students对象的引用,此时浅拷贝就是会建立schoolname的副本和对students对象指针的副本(并不会建立一个全新的students对象),而深拷贝则会建立一个全新的students对象副本出来
9 请说明Equals和==的区别。
       C#中的相等有两种类型:引用相等(ReferenceEquals)和值相等(Equals)。值相等就是说两个对象包含相同的值。而引用相等则比较的是两个对象的引用是不是同一个对象。也就是说,若是ReferenceEquals为True,则Equals必然为True,反过来就不必定了 
       ==运算符(引用相等):经过过判断两个引用是否指示同一对象来测试是否相等。
      Equals是要实现值相等比较的,为object类的方法,一个类默认状况下,在不重写基类Equals方法的时候对比的是对象的引用是否指向同一块内存地址。重写Equals函数的目的是为了比较两个对象的value值是否相同,例如利用equals比较八大包装对象(如int,float等)和String类(由于该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址(例如自定义的类,由于没有重写equals方法)。
如何重写equals方法:
主要用于经过对比自定义类中的每一个成员来对比两个类是否值相等;重写equals也要重写hashcode方法
在建立1个对象的时候会自动为这个对象建立hashcode,这样若是咱们对2个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,此时在未重写hashcode方法的时候,这两个对象却拥有不一样的hashcode,由此将产生了理解的不一致,在存储散列集合时(如Set类),将会存储了两个值同样的对象,致使混淆,所以,就也须要重写hashcode()
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的,在这里对比两个对象相同时,其中最早对比的是对象的hashcode,若是两个对象的hashcode不一样,则这两个对象必然不一样,从而大大提升了效率
10 面向对象三大特征:
封装:封装就是将数据或函数等集合在一个个的类中,被封装的对象一般被称为抽象数据类型。封装的意义在于保护或者防止代码(数据)被咱们无心中破坏。例如咱们会将属于类自己的行为,字段或方法定义为private,外部不能能访问它。使用属性来控制对数据字段的修改或只读只写。访问修饰符:Private,Protected(类和派生类能够存取),Internal(只有同一个项目中的类能够存取),Public
继承:继承主要实现代码间的纵向联系,继承了父类的子类也会有父类的行为。
1、C#中的继承符合下列规则:
    1. 继承是可传递的。若是C从B中派生,B又从A中派生,那么C不只继承了B中声明的成员,一样也继承了A中的成员。Object类做为全部类的基类。
    2. 派生类应当是对基类的扩展。派生类能够添加新的成员,但不能除去已经继承的成员的定义。
    3. 构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类可否访问它们。
    4. 派生类若是定义了与继承而来的成员同名的新成员,就能够覆盖已继承的成员。但这并不由于这派生类删除了这些成员,只是不能再访问这些成员。
    5. 类能够定义虚文法、虚属性以及虚索引指示器,它的派生类可以重载这些成员,从而实现类能够展现出多态性。
  2、new关键字
   若是父类中声明了一个没有friend修饰的protected或public方法,子类中也声明了同名的方法。则用new能够隐藏父类中的方法。(不建议使用)
  3、base关键字 base 关键字用于从派生类中访问基类的成员:
    1. 调用基类上已被其余方法重写的方法。
    2. 指定建立派生类实例时应调用的基类构造函数。
多态:(重载就是多态)
编译时的多态性:
  编译时的多态性是经过重载来实现的。对于非虚的成员来讲,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操做。
  运行时的多态性:运行时的多态性就是指直到系统运行时,才根据实际状况决定实现何种操做。C#中,运行时的多态性经过虚成员实现。编译时的多态性为咱们提供了运行速度快的特色,而运行时的多态性则带来了高度灵活和抽象的特色。
   2、实现多态:接口多态性。继承多态性。经过抽象类实现的多态性。
  3、override关键字:重写父类中的virtual修饰的方法,实现多态。
 
11 请谈一下你对Exception的理解
在 C# 中,程序中的运行时错误经过使用一种称为“异常”的机制在程序中传播。异常由遇到错误的代码引起,由可以更正错误的代码(try catch)捕捉。 一旦引起了一个异常,clr自上而下搜索匹配的catch块,一个try块对应多个catch块,越具体的catch应该在上面,接着是她们的基类型,最后是exception类,未指定也是exception类型。若是上下顺序反了,那么具体的catch块会执行不到,编译器会报错。try catch 之后,若是只是处理了捕获的异常(例如记日志),没有抛出异常(throw),后续代码会继续执行
throw是抛出一个异常,中断执行后续代码,若是一个方法可能会有异常,但你不想处理这个异常,就是用throw,谁调用了这个方法谁就要处理这个异常,或者继续抛出。
throw和throw ex
1 throw ex 抛出与catch捕捉到的相同的异常对象,致使clr重置该异常的起点。认为你catch到的异常已经被处理了,只不过处理过程当中又抛出新的异常,从而找不到真正的错误源 try{ }catch (Exception ex){ throw ex; }
2 throw 从新抛出异常对象,clr不会重置起点(推荐使用这个)try{} catch{ throw;}
3 或者对异常就行从新包装,保留原始异常点信息,而后抛出
finally:
不管代码有没有抛出异常,finally块始终会执行。应该先用finally块清理那些已经成功的操做,还能够用finally显示释放对象避免资源泄露。Finally 块使程序员可以清除停止的 try 块可能遗留下的任何模糊状态,或者释听任何外部资源(例如图形句柄、数据库链接或文件流),而无需等待运行时中的垃圾回收器终结这些对象.
使用了 lock,using,foreach的语句,c#编译器会自动加上try finally块,并在finally中自动释放资源
 
能够在一个线程中补获异常,在另一个线程中从新抛出异常;
clr在调用栈中向上查找与抛出的异常对象类型匹配的catch块,没有任何catch块匹配抛出的异常类型,就会发生一个未处理的异常
 
十二、你平时经常使用的集合类有哪些? 分别有什么区别?请结合实际场景说明你在项目中的使用状况。
               System.Collections命名空间包含各类集合对象和接口的定义(如列表、队列、位数组、哈希表和字典)System.Collections.Generic 命名空间包含定义泛型集合的接口和类,泛型集合容许用户建立强类型集合,它能提供比非泛型强类型集合更好的类型安全性和性能。
数组、arraylist、list、hashtable、directonary、stack堆栈、quene队列
数组:数组是固定大小的,不能伸缩,数组要声明元素的类型
a ArrayList动态数组能够存储任何类型,不像List泛型那样有严格要求,ArrayList就至关因而list<object>,List类在大多数状况下执行得更好而且是类型安全的。注意list非线程安全的,多线程中操做一个list最好使用ConcurrentList,和ConcurrentDictionary同样内部自带加速机制
b HashTable应用场合:作对象缓存,树递归算法的替代,和各类需提高效率的场合。HashTable中的key/value均为object类型,由包含集合元素的存储桶组成。HashTable的优势就在于其索引的方式,速度很是快。若是以任意类型键值访问其中元素会快于其余集合,特别是当数据量特别大的时候,效率差异尤为大。
c Hashtable和Dictionary<K, V>类型  
1:单线程中推荐使用Dictionary,有泛型优点,且读取速度较快,容量利用更充分.  
2:多线程中推荐使用Hashtable,默认Hashtable容许单线程写入, 多线程读取对Hashtable进一步调用Synchronized()方法能够得到彻底线程安全的类型,而Dictionary非线程安全,必须人为使用lock语句进行保护, 效率大减。  .net提供了另一个字典类ConcurrentDictionary,是线程安全的支持并发操做
3:Dictionary有按插入顺序排列数据的特性(注:但当调用Remove()删除过节点后顺序被打乱), 所以在须要体现顺序的情境中使用Dictionary能得到必定方便。
d HashTable、HashMap、HashSet之间的区别
HashTable是基于Dictionary的key value集合,是线程安全的
HashMap是基于Map接口的一个实现,HashMap能够将空值做为一个表的条目的key或者value,HashMap中因为键不能重复,所以只有一条记录的Key能够是空值,而value能够有多个为空,但HashTable不容许null值(键与值均不行)
HashSet 是set的一个实现类,存储的是一个对象列表,底层采用的是HashMap进行实现的,可是没有key-value,只有HashMap的key set的视图,HashSet不允许重复的对象
 
1三、什么是接口,什么是抽象类?两者有什么区别?请结合实际场景说明你在项目中的使用状况。
抽象类和接口都是对业务的一种抽象方式。
一、接口定义了一种行为,一种规则,它告诉了你要作什么,但不会去具体实现。当一个类继承了这个接口的时候,它将负责实现这个接口
 二、当类选择实现某个接口的时候,类必须将这个接口所有实现,不能只实现其中一部分。
 三、接口和抽象类的区别:一个类只能继承一个父类,但一个类能够同时实现多个接口,格式为: 类名:接口1,接口2
 四、接口里的成员方法(行为),没有修饰符,但接口自己是能够有修饰符的   
 五、抽象类的做用:负责定义“实现和部分的动做”(抽象类中能够实现某些方法),接口只负责定义动做不负责实现
interface的应用场合:
  1. 类与类以前须要特定的接口进行协调,而不在意其如何实现。
  2. 做为可以实现特定功能的标识存在,也能够是什么接口方法都没有的纯粹标识。
  3. 须要将一组类视为单一的类,而调用者只经过接口来与这组类发生联系。
  4. 须要实现特定的多项功能,而这些功能之间可能彻底没有任何联系。
abstract class的应用场合:一句话,在既须要统一的接口,又须要实例变量或缺省的方法的状况下,就可使用它。最多见的有:
  1. 定义了一组接口,但又不想强迫每一个实现类都必须实现全部的接口。能够用abstract class定义一组方法体,甚至能够是空方法体,而后由子类选择本身所感兴趣的方法来覆盖。
  2. 某些场合下,只靠纯粹的接口不能知足类与类之间的协调,还必需类中表示状态的变量来区别不一样的关系。abstract的中介做用能够很好地知足这一点。
  3. 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,能够共享的,无需子类分别实现;而另外一些方法却须要各个子类根据本身特定的状态来实现特定的功能
 典型的是包含主订单和子订单的系统中应该使用抽象类,由于他们有共享的业务逻辑不须要子订单类实现,同时也有各子订单必须实现订单的操做约定
1四、什么是进程,什么是线程,什么是AppDomain?三者有什么区别?请结合实际场景说明你在项目中的使用状况。
答:进程是系统进行资源分配和调度的单位;线程是CPU调度和分派的单位,一个进程能够有多个线程,这些线程共享这个进程的资源。
               AppDomain表示应用程序域,它是一个应用程序在其中执行的独立环境。是CLR的运行单元,它能够加载Assembly、建立对象以及执行程序。AppDomain是CLR实现代码隔离的基本机制。AppDomain被建立在进程中,一个进程内能够有多个AppDomain。一个AppDomain只能属于一个进程。AppDomain是个静态概念,只是限定了对象的边界;线程是个动态概念,它能够运行在不一样的AppDomain。一个AppDomain内能够建立多个线程,可是不能限定这些线程只能在本AppDomain内执行代码。每一个appdomain都有一个托管堆,每一个托管堆内有两块区域,一块存储对象,一块存储对象元数据(静态成员)。
1五、请谈一下你对泛型的理解。请结合实际场景说明你在项目中的使用状况。
  一、所谓泛型,即经过参数化类型来实如今同一份代码上操做多种数据类型,泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更灵活的复用。 C#泛型赋予了代码更强的类型安全,更好的服用,更高的效率,更清晰的约束。
泛型的意义何在?类型安全和减小装箱、拆箱并非泛型的意义,而是泛型带来的两个好处而已(或许在.net泛型中,这就是明显的好处) 泛型的意义在于—把类型做为参数,它实现了代码见的很好的横向联系,咱们知道继承为了代码提供了一种从上往下的纵向联系, 但泛型提供了方便的横向联系(从某种程度上说,它和AOP在思想上有相同之处)
二、泛型类型参数:在【泛型类型】或【方法定义】中,类型参数是客户端在实例化泛型类型的变量时指定的特定类型的占位符。就是那个list<T>中的T
类型参数的约束:在定义泛型类时,能够对客户端代码可以在实例化类时用于类型参数的类型种类施加限制。 若是客户端代码尝试使用某个约束所不容许的类型来实例化类 ,则会产生编译时错误。 这些限制称为约束。 约束是使用 where 上下文关键字指定的。 采用“基类、接口、构造器、值类型、引用类型”的约束方式来实现对类型能数 的“显式约束“, C#泛型要求对“全部泛型类型或泛型方法的类型参数”的任何假定,都要基于“显示的约束”,以维护C# 所要求的类型安全
1六、请谈一下你对同步和异步,多线程的理解。请结合实际场景说明你在项目中的使用状况。
并发:同时处理多件事情,在处理第一个请求时同时响应第二个请求;
同步:同步就是顺序执行,执行完一个再执行下一个,须要等待、协调运行,同步 调用在继续以前等待响应或返回值。若是不容许调用继续,就说调用被阻塞 了
异步:并发的一种形式,(1)它采用回调机制,避免产生没必要要的线程。(2)多线程也能够成为实现程序异步的一种方式,在这里 异步和多线程并非一个同等关系,异步是最终目的,多线程只是咱们实现异步的一种手段(这违反了异步操做的本质)
多线程:并发的另外一种形式,它采用多个线程来执行程序。对多个线程的管理使用了线程池。
并行处理:把正在执行的大量任务,分割成小块分配个多个运行的线程,线程池是存听任务的队列,这个队列能根据须要自行调整。由此产生了并行处理这个概念,多线程的一种,而多线程是并发的一种
异步优缺点:
由于异步操做无须额外的线程负担,而且使用回调的方式进行处理,在设计良好的状况下,处理函数能够没必要使用共享变量(即便没法彻底不用,最起码能够减小 共享变量的数量),减小了死锁的可能。固然异步操做也并不是完美无暇。编写异步操做的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思惟方式有些出入,并且难以调试。
多线程优缺点:
多线程的优势很明显,线程中的处理程序依然是顺序执行,符合普通人的思惟习惯,因此编程简单。可是多线程的缺点也一样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。而且线程间的共享变量可能形成死锁的出现
二者适用范围:
在了解了线程与异步操做各自的优缺点以后,咱们能够来探讨一下线程和异步的合理用途。我认为:当须要执行I/O操做时,使用异步操做比使用线程+同步 I/O操做更合适。I/O操做不只包括了直接的文件、网络的读写,还包括数据库操做、Web Service、HttpRequest以及.net Remoting等跨进程的调用。
  而线程的适用范围则是那种须要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。可是每每因为使用线程编程的简单和符合习惯,因此不少朋友每每会使用线程来执行耗时较长的I/O操做。这样在只有少数几个并发操做的时候还无伤大雅,若是须要处理大量的并发操做时就不合适了。
17 线程池和TPL
线程池能管理不少个线程。和手动建立一个线程来执行特定操做不一样,咱们将工做任务扔到线程池中,它会选择合适的线程,而后去执行咱们给定的方法。线程池经过限制建立线程的总数,根据给定的工做量来决定建立合适的线程数量,下降了在极端状况下,如处理量比较少的状况下建立和销毁线程的开销,帮助咱们解决了对系统资源的独占和过分使用。
TPL=>TASK Parallel Library 并行库,它是基于线程池的, 一种框架要使得并行执行不会产生太多的线程,可以保证工做量可以平均的分配到全部线程上,而且可以报告错误和产生可靠地结果,这就是并行库Task Parallel Library的工做,任务并行化是指经过一系列API将大的任务分解成一系列小的任务,而后再多线程上并行执行 (TPL)并行库有一系列API可以基于线程池同时管理成千上万个任务。TPL的核心是System.Threading.Tasks类,他表明一个小的任务,“Task是一种对线程和线程池里面的工做项的一种结构话抽象
1八、请说明using和new的区别。
new有两种用法:1.实例化一个对象 2.声明隐藏方法
一、using导入其它命名空间中定义的类型,这样,您就没必要在该命名空间中限定某个类型的使用
二、using为命名空间或类型建立别名 using Project = PC.MyCompany.Project;  (2)为类去别名,右边不能有开放式泛型类型list<T>,必须会LIST<INT>这种  
三、using提供一种能确保正确使用Idisposable对象的比较方便的语法;(idisposable接口有一个方法DIspose()咱们通常用它释放对象占用的资源),它定义了一个范围,在此范围内的末尾将执行dispose方法(即便范围内发生异常也会执行dispose()方法)
 1九、 dynamic 动态类型:标记了dynamic标记的表达式在运行时才会被解析。编译时,不会被解析。能够用dynamic表达式调用 类型成员。它的出现方便了开发人员使用反射或者与其余非c#组建通讯(例如com,例如html dom)。dynamic被编译后,实际是一个 object类型,只不过编译器会对dynamic类型进行特殊处理,让它在编译期间不进行任何的类型检查,而是将类型检查放到了运行期。
 20、反射和序列化:
容许在运行时发现并使用编译时还不了解的类型及成员
1 因为反射严重依赖字符串,形成反射在编译时没法保证类型安全,例如Type.GetType("int")
2 反射速度慢,使用反射时类型及其成员的名称在编译时未知,你要用字符串名称标识每一个类型及其成员,而后在运行时发现他们。反射机制会不停的在程序集的元数据中执行字符串搜索,字符串搜索执行的是不区分大小写的比较。从而影响速度。使用反射调用方法也会影响性能。尽可能避免使用反射来访问字段或调用方法
反射:程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可使用反射动态地建立类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。而后,能够调用类型的方法或访问其字段和属性
序列化:序列化是将对象转换为容易传输的格式的过程。例如,能够序列化一个对象,而后使用 HTTP 经过 Internet 在客户端和服务器之间传输该对象。在另外一端,反序列化将从该流从新构造对象。
 2一、特性:
特性是为程序添加元数据的一种机制(存储在程序文件里的元数据),特性自己就是用来描述数据的元数据(metadata),经过它能够给编译器提供指示或者提供对数据的说明,特性是一个对象,它能够加载到程序集及程序集的对象中,这些对象包括 程序集自己、模块、类、接口、结构、构造函数、方法、方法参数等,加载了特性的对象称做特性的目标。
.net内置的常见特性有:ObsoleteAttribute,使用方式:[Obsolete("该方法已通过期")] 此时在你调用打了标记的方法时,会发出警告消息,来提醒你该方法已通过期
自定义特性:
一、自定义特性类与定义一个类没有什么区别,只须要继承自Attribute基类便可
二、特性类(metadata)自己须要三个特性去描述它Serializable、AttributeUsage 和 ComVisible,因为特性自己就是用来描述数据的元数据,因此描述元数据的特性被又称为元元数据meta-metadata,大多数状况下咱们只须要掌握AttributeUsage 特性就行了,该特性有三个参数:
ValidOn:这是特性应用位置参数,定义了一个自定义特性可以应用到那种编程元素上,是方法仍是属性
AllowMultiple:命名参数,它指定一个特性可否在一个程序集中被屡次使用
Inherited:命名参数,用于控制一个自定义特性是否能被该类型的子类继承
其中,位置参数ValidOn使用AttributeTargets枚举
例如:[AttributeUsage(AttributeTargets.Method)] //控制自定义特性的使用,该特性就只能用于方法上面{能够不设置}
通常经过反射来遍历检查应用的特性信息,咱们能够借助该信息来作权限,安全,登陆状态验证
21 扩展方法:
若是要给已编译好的类添加新功能,开发人员通常只能从这个类中派生一个基类,或者更改这个类的源代码方式;
扩展方法的出现解决了这个问题,咱们能够在一个单独的类中对已经存在的类进行扩展,为其添加扩展方法并不须要对源代码进行改动或继承基类
定义扩展方法:
* 一、扩展方法必需要定义到一个静态类中,扩展方法自己也必须是静态的
* 二、扩展方法首个参数必须是this 后面紧跟扩展的类 如:static void fun (this int i)
* 三、扩展方法能够被正确的对象实例调用 也可使用静态类名进行静态调用
* 四、扩展方法的名称不能与要扩展的类名相同 编译时,扩展方法的优先级要低于扩展类自己方法的优先级
* 五、不能再扩展方法中直接访问扩展类的成员变量
2二、 表达式树
表达式树提供一个将可执行代码转换成数据的方法,若是你要在执行代码以前修改或转换此代码他将变得颇有价值。
表达式树被建立是为了制造一个像将查询表达式转换成字符串以传递给其余程序并在那里执行这样的转换任务。把代码,转换成数据,而后分析数据发现其组成部分,最后转换成能够传递到其余程序的字符串,Linq to SQL,实际上它就是C#做为源语言,SQL做为目标语言的一个编译过程, 一个LINQ to SQL查询不是在你的C#程序里执行的。它会被转换成SQL,经过网络发送,最后在数据库服务器上执行
表单式树有四个属性:
* Body:获得表达式的主体
* Parameters:获得lambda表达式的参数
* NodeType:获取树的节点的ExpressionType共45种不一样值,包含全部表达式节点各类可能的类型,例如返回常量,例如返回参数,例如取两个值的小值(<),例如取两个值的大值(>),例如将值相加(+),等等。
 
2三、在平常开发中你经常使用哪些设计模式?分别解决什么问题?请结合实际场景说明你在项目中的使用状况。
简单工厂、工厂方法、策略模式、抽象工厂模式、单利模式(将对象的构造函数设置为私有的,阻止外界直接实例化它,必须经过访问静态实例化属性/方法来建立)
24 CLR名词解释
CLR(common language runtime)公共语言运行时:clr 公共语言运行时,核心功能是内存管理,程序集加载,安全性,异常处理和线程同步。面向clr的全部语言均可以使用核心功能。在运行时clr不关心开发人员使用的是什么语言开发的源代码,由于针对每种语言都有一个面向clr的代码编译器,例如c#有针对c#的编译器,c++也有它本身的编译器。编译器将其最终生成托管模块。
托管模块: 托管模块是32位或者64位的windows可移植执行体pe32文件。须要clr才能运行。它包含四部份内容
程序集和托管模块:clr实际不和托管模块工做,它和程序集工做。程序集是一个抽象概念,他是一个或多个托管模块/资源文件的逻辑分组,程序集是重用和安全性以及版本控制的最小单元
托管代码和非托管代码:
在CLR监视下运行的程序属于“托管代码”,而再也不CLR监视下直接在裸机上运行的程序属于“非托管代码”,非托管代码能够对系统进行低级控制,能够按照本身的想法管理内存,更方便的建立线程。c++编译器默认生成包含非托管代码的模块,并在运行时操做非托管内存数据。这些模块不须要clr就可运行,c++的编译器是独一无二的,它容许开发人员同时写托管代码和非托管代码,并生成到同一模块中。
FCL(framework class library) framework类库:
是一组DLL程序集的统称,是微软发布的数千个类型的定义,包含了不少辅助功能。开发人员能够利用这些程序集建立应用程序,例如:windows控制台,windows服务,web应用等
CTS (common type system) 通用类型系统:
CLR一切都围绕类型展开,类型向应用程序和其余类型公开功能,类型是clr的根本,因此微软制订了正式的规范来描述类型的定义和行为;例如cts规范规定了一个类型能够有0个或多个成员,成员包括字段,方法,属性,事件。cts他还规定了类型和成员的访问规则,例如标记为public仍是product等。
CLS (common language specification)公共语言运行规范:
CLR集成了全部的语言,用一个语言建立的对象在另一个语言中具备和它本身语言建立的对象同等的地位,可是各类语言在语法上存在很大差异,要建立其余语言都能访问的类型只能从自身语言床挑选其余语言可识别的功能;微软提供了一种语法规则,凡是符合cls公共语言容许规范的功能就能在其余语言中一样符合其规范的功能访问。它是cts/clr的一个子集。
 
 
实践题:
相关文章
相关标签/搜索