常用笔试题,可任选十题作为第一步筛查新手基础是否牢固。
简述 private、 protected、 public、 internal 修饰符的访问权限。
答 . private : 私有成员, 在类的内部才可以访问。
protected : 保护成员,该类内部和继承类中可以访问。
public : 公共成员,完全公开,没有访问限制。
internal: 访问仅限于当前程序集。
internal,英文含义是“内部的”,这时候基础不扎实的同学,可能就混淆了这个“内部的”的确切含义,到底是指“同一命名空间”的内部,还是“同一程序集”的内部,网上不少面试题答案以为就是“同一命名空间”的内部,其实只要MSDN查一下,就很清楚了)。
扩展知识:protected internal:访问限制到当前程序集或从包含派生的类型的类别。
在同程序集下,protected internal与internale相同,体现的是internal的性质,即在派生类类内、类外均可访问继承的基类使用protected internal修饰的成员变量。
在非同程序集下,protected internal与protected相同,体现的是protected的性质,即在只有在派生类的类内通过派生类实例的成员变量才能访问继承的基类使用protected修饰的成员变量。
考点:考查新手基础,只要写过真实程序或认真学习过,都能答得上来,internal用的较少
C#中的委托是什么?事件是不是一种委托?
答 : 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
在c++里有指针,委托可以理解为指向一个函数的引用。
事件是一种特殊的委托,事件有sender(发送方),但是事件不知道接受方是谁,这时候就需要委托去指向对应的接收方。
简单示例:
public delegate void MyDel();//自定义一个委托
class Program
{
static void Main(string[] args)
{
//简单委托
MyDel say1 = SayHi;
MyDel say2 = new MyDel(SayHi);
say1();
say2();
say1.invoke();
}
static void SayHi()
{
Console.WriteLine("Hi World");
}
}
1、Delegate.Invoke (委托同步调用)
a、委托的Invoke方法,在当前线程中执行委托。
b、委托执行时阻塞当前线程,直到委托执行完毕,当前线程才继续向下执行。
c、委托的Invoke方法,类似方法的常规调用。
2、Delegate.BeginInvoke (委托异步调用)
a、委托的BeginInvoke方法,在线程池分配的子线程中执行委托
b、委托执行时不会阻塞主线程(调用委托的BeginInvoke线程),主线程继续向下执行。
c、委托执行时会阻塞子线程。
d、委托结束时,如果有返回值,子线程讲返回值传递给主线程;如果有回调函数,子线程将继续执行回调函数。
override与重载的区别
答 :override 与重载的区别。重载是方法的名称相同。参数或参数类型不同,进行多
次重载以适应不同的需要
Override是进行基类中函数的重写。为了适应需要。
c#的abstract和virtual的用法和区别
virtual和abstract都是用来修饰父类的,通过覆盖父类的定义,让子类重新定义。它们有一个共同点:如果用来修饰方法,前面必须添加public,要不然就会出现编译错误:虚拟方法或抽象方法是不能私有的。毕竟加上virtual或abstract就是让子类重新定义的,而private成员是不能被子类访问的。
abstract方法就是抽象方法,有以下特点:
1、抽象方法就是没有实现的,必须是形如:public abstract void Init();
2、拥有抽象方法的类必须修饰一个abstract关键字从而变成一个抽象类;但是反过来,抽象类里面不一定要有抽象方法,比如我写了一个非抽象类,但是这个类我不想让人直接实例化,而只让人继承,我就可以把他变成一个抽象类,虽然他里面并没有抽象方法。形如:abstract class TestAbstractSuper
3、抽象类不能被实例化,只能被继承。
4、抽象类的子类必须实现每个抽象方法。
5、抽象类里面除了可以有抽象方法以外,也可以有普通的方法。
6、抽象类的构造函数可以省略,编译器会自动加上,但是不能是一个抽象方法,而只能是一个普通的构造函数。
小结:简单概括一下,抽象类不可以直接实例化,他可以有n个(n>=0)抽象方法,这些抽象方法子类必须实现。
再说virtual,虚拟类,他有几个特点:
1、声明了virtual的方法无需去改动类的声明,他只在此方法上起到影响。
2、只有virtual的方法可以被子类override。
3、子类可以不ouverride父类的virtual方法,这种情况下他就像普通的父类方法一样。
小结:简单概括一下,virtual关键字就是告诉子类,此方法可以被override,但非强制。
对比:
virtual修饰的方法必须有实现(哪怕是仅仅添加一对大括号),而abstract修饰的方法一定不能实现。
virtual可以被子类重写,而abstract必须被子类重写。
如果类成员被abstract修饰,则该类前必须添加abstract,因为只有抽象类才可以有抽象方法。
无法创建abstract类的实例,只能被继承无法实例化,比如: BaseTest2 base2 = new BaseTest2();将出现编译错误:抽象类或接口不能创建实例。
C#中如果要在子类中重写方法,必须在父类方法前加virtual,在子类方法前添加override,这样就避免了程序员在子类中不小心重写了父类方法。
abstract方法必须重写,virtual方法必须有实现(即便它是在abstract类中定义的方法).
抽象方法不能使用private、static和virtual修饰符。(抽象的方法默认是一个virtual方法)
抽象方法不能在派生类用base关键字进行访问。
抽象方法声明不提供实际的实现,没有方法体。若要实现抽象方法,需要在派生类(非抽象类)中进行重写该抽象方法,继承类只有实现过所有抽象类的抽象方法后才能被实例化。
构造函数是否可被override? 答:构造函数不能被继承,子类会自动调用父类的无参构造函数。因此不能重写override,但可以被重载
是否可以继承String类?
答:String类是sealed
类故不可以继承。
能用foreach遍历访问的对象需要实现 ________________接口或声明________________方法的类型。
答:IEnumerable 、 GetEnumerator。
考点:
IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumerator。Enumerable这个静态类型含有很多扩展方法,其扩展的目标是IEnumerable<T>。
实现了这个接口的类可以使用Foreach关键字进行迭代(迭代的意思是对于一个集合,可以逐一取出元素并遍历之)。实现这个接口必须实现方法GetEnumerator。
写一个方法来处理一组数据项,最好是用接口(比如IEnumerable)来声明方法的参数,而不是要写强数据类型(比如List)或者更强的接口类型(比如ICollection或者IList)
对泛型的理解
泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用
泛型三大好处:类型安全,增强性能,代码复用。类型安全和减少装箱、拆箱。提高性能、类型安全和质量,减少重复性的编程任务
什么是装箱和拆箱? 答:从值类型接口转换到引用类型装箱。从引用类型转换到值类型拆箱。
.net中读写数据库需要用到那些类?他们的作用?
答:DataSet:数据存储器。
DataCommand:执行语句命令。
DataAdapter:数据的集合,用语填充。
考点:以前的数据库操作常用类,现在ORM框架很多,Code First都已经基本用不到这些了,除一些技术未更新的公司
在C#中,string str = null 与 string str = “” 区别。 答:string str = null 只是定义了一个string 类的引用,str并没有指向任何地方,而string str = "" 给它分配长度为空字符串的内存空间。
面向对象的语言具有________性、_________性、________性 答:封装、继承、多态。
封装,就是把客观的事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的信息隐藏。隐藏实现细节,使得代码模块化。
继承,可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。可以扩展已存在的代码模块。
多态,不同类的对象对同一消息做出响应。同一消息可以根据发送对象的不同而采用多种不同的行为方式。多态存在的三个必要条件:继承、重写、父类引用指向子类对象。
反射理解
反射提供了封装程序集、模块和类型的对象(Type类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了特性,可以利用反射对它们进行访问。可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。
优点:
- 1、反射提高了程序的灵活性和扩展性。
- 2、降低耦合性,提高自适应能力。
- 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:
- 1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
- 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。
示例:
public interface iDemo_Result
{
string getResult(string param);
}
public class Fun_A:iDemo_Result
{
public string getResult(string param)
{
return "执行 Fun_A类中的getResult方法";
}
}
public class Fun_B: iDemo_Result
{
public string getResult(string param)
{
return "执行 Fun_B类中的getResult方法";
}
}
反射代码:
public void Run()
{
getResult("DemoResult", string.Empty);
getResult("Fun_B", string.Empty);
}
public void getResult(string url, string param)
{
iDemo_Result inteface;
Type t = Type.GetType("ReflectionStudy." + url, true, true);
inteface = (iDemo_Result)System.Activator.CreateInstance(t);
string result = inteface.getResult(param);
Console.WriteLine(result);
}
序列化和反序列化
序列化:将对象转换为另一种媒介传输的格式过程。如,序列化一个对象,用Http通过internet在客户端和服务器之间传递该对象,在另一端用反序列化从该流中重新得到对象。
接口的理解
接口是一种引用类型,在接口中可以声明方法.属性.索引器和事件,但或。
接口是一种用来定义程序的协议,它描述可属于任何类或结构的一组相关行为。接口可有方法、属性、事件和索引器或这四种成员的任何组合类型,但不能包含字段。
那么接口具有哪些特点呢?
·接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员(说明:如类A继承接口B,那么A中必须实现B中定义的属性,方法等)。
·不能直接实例化接口
·接口可以包含事件、索引器、方法和属性
·接口不包含方法的实现
·类和接口可以从多个接口继承
·接口自身可以继承多个接口
不可以声明公有的域
在声明接口时除了Interface和接口名称是必须的,其他都是可选项。另可使用new、public、protected、intenal和private等修饰符实现接口,但接口成员必须是公共的,不可以声明私有的成员变量
对比抽象基类和接口的使用
如果要让自定义类包含基类的实现,应该选用抽象基类,接口多做契约使用。.NET中类只允许单继承,但接口允许多继承。
关键字:abstract
语法:
abstract class 类名
{
…………
}
1、一个抽象类可以同时包含抽象方法和非抽象方法。
2、抽象方法只在派生类中真正实现,这表明抽象方法只存放函数原型,不涉及主体代码,
3、派生自抽象类的类需要实现其基类的抽象方法,才能实例化对象。
4、使用override关键子可在派生类中实现抽象方法,经override声明重写的方法称为重写基类方法,其签名必须与override方法的签名相同。
示例:
using System;
namespace Example_5
{
//抽象类
abstract class ABC
{
//抽象方法
public abstract void Afunc();
}
//派生类
class Derv:ABC
{
//实现抽象类中的抽象方法
public override void Afunc()
{
Console.WriteLine(“实现抽象方法”);
}
}
public class Test
{
static void Main(string[] args)
{
Derv obj=new Derv();
obj.Afunc();
}
}
}
5、基类实现抽象类,则派生类不需要重新实现该抽象类。
6、抽象类并不仅仅只是一种实现技巧,它更代表一种抽象的概念,从而为所有的派生类确立一种约定。
关键字:interface
语法:
修饰符 interface 接口名
{
//接口主体
}
1、一个接口就相当于一个抽象类,但是它不能包含任何实现方法。
2、接口的每种方法都必须在派生类中实现。
3、接口有时候可以看成是类的模具,它指明一个类该提供哪些内容。
4、接口主体只限于方法、索引器、属性的声明。
5、接口中不能包含字段、构造函数和常量等。
6、接口成员是隐式公开的,如果对其显式指定访问级别,就会出现编译器错误。
7、在接口中不能实现任何方法,属性或者索引器。
8、在指定方法时,只需给出返回类型、名称和参数列表,然后以分号结束。
9、实现接口的语法与实现继承一样,都用冒号“:”
示例
interface Icustomer
{
………………
}
public class MyClass: Icustomer
{
………………
}
10、接口中的方法不能重写,只能实现。
接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承实体类,但前提是实体类必须有明确的构造函数。
什么叫应用程序域?什么是受管制的代码?什么是托管代码?什么是强类型系统?什么是装箱和拆箱?什么是重载?CTS.CLS和CLR分别作何解释?
应用程序域
为安全性.可靠性.版本控制以及卸载程序集提供了隔离边界。应用程序域通常由运行库宿主创建,运行库宿主负责在运行应用程序之前引导公共语言运行库。应用程序域提供了一个更安全.用途更广的处理单元,公共语言运行库可使用该单元提供应用程序之间的隔离。
受管制的代码
在.Net环境中运行的任何代码都是受管制的代码(managed code),.Net外部的代码也运行在windows上,这些代码称为未受管制的代码(unmanaged code)。
使用基于公共语言运行库的语言编译器开发的代码称为托管代码;托管代码具有许多优点,例如:跨语言集成.跨语言异常处理.增强的安全性.版本控制和部署支持.简化的组件交互模型.调试和分析服务等。
强类型语言
是能够禁止任何违反类型系统的代码的语言,或者说是能够捕获所有违反类型系统的错误的语言。我们说C++相对于C是强类型的,是因为C++禁止了一些C中的隐式转换,比如将void*转换为任意的指针类型。
装箱和拆箱
使值类型能够被视为对象。对值类型装箱将把该值类型打包到 Object 引用类型的一个实例中。这使得值类型可以存储于垃圾回收堆中。拆箱将从对象中提取值类型。
每个类型成员都有一个唯一的签名。方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。只要签名不同,就可以在一种类型内定义具有相同名称的多种方法。当定义两种或多种具有相同名称的方法时,就称作重载。
CTS通用类型系统 (common type system)
一种确定公共语言运行库如何定义.使用和管理类型的规范。
CLR公共语言运行库
.NET Framework 提供了一个称为公共语言运行库的运行时环境,它运行代码并提供使开发过程更轻松的服务。
CLS公共语言规范
要和其他对象完全交互,而不管这些对象是以何种语言实现的,对象必须只向调用方公开那些它们必须与之互用的所有语言的通用功能。为此定义了公共语言规范 (CLS),它是许多应用程序所需的一套基本语言功能。
写一个标准的lock(),在访问变量的前后创建临界区,要有“双重检查”
public sealed class Singleton
{
private static Object s_lock = new Object();
private static Singleton s_value;
//私有构造器组织这个类之外的任何代码创建实例
private Singleton() { }
// 下述共有,静态属性返回单实例对象
public static Singleton Value
{
get
{
// 检查是否已被创建
if (s_value == null)
{
// 如果没有,则创建
lock (s_lock)
{
// 检查有没有另一个进程创建了它
if (s_value == null)
{
// 现在可以创建对象了
s_value = new Singleton();
}
}
}
return s_value;
}
}
}
双检锁(double-check locking)技巧
lock为什么要锁定一个参数,可不可锁定一个值类型?这个参数有什么要求?
lock的锁对象要求为一个引用类型。她可以锁定值类型,但值类型会被装箱,每次装箱后的对象都不一样,会导致锁定无效。
对于lock锁,锁定的这个对象参数才是关键,这个参数的同步索引块指针会指向一个真正的锁(同步块),这个锁(同步块)会被复用。
using的用途和使用技巧.
1、 引用命名空间
2、 为命名空间或类型创建别名
3、 使用using语句定义作用范围,出范围后自动调用Dispose方法回收资源,在程序编译阶段,编译器会自动将using语句生成try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源.所以,using语句等效于try-finally语句
单例模式的特点:
1、只能有一个实例;
2、只能自己创建自己的唯一实例;
3、必须给所有其他的对象提供这一实例。
没有考虑多线程安全问题
public class Singleton { private static Singleton _instance = null; private Singleton(){} public static Singleton CreateInstance() { if(_instance == null) { _instance = new Singleton(); } return _instance; } }
考虑多线程安全
public class Singleton { private volatile static Singleton _instance = null; private static readonly object _lockrobject = new object(); private Singleton(){} public static Singleton CreateInstance() { if(_instance == null) { lock(_lockrobject ) { if(_instance == null) _instance = new Singleton(); } } return _instance; } }
C#,自己主动创建实例
public class Singleton { private Singleton(){} public static readonly Singleton instance = new Singleton(); }
C#中override和new有什么区别?什么时候用new?
override:派生类覆盖基类的方法,通过基类base调用这个方法,调用的是派生类覆盖的。
new:派生类定义一个新的方法,通过基类base调用这个方法,调用的是基类的。
new是为了避免冲突而设计的,这种场合很罕见,但是存在。比如说派生类需要实现一个接口,不得不定义一个和基类重名的方法。最好不要用new,除非你必须定义一个重名的方法,但是最好另外换个名字。