⼀个从C++初级到C#⾼级的⾯试历程
  基础概念和常⽤框架(关键点)
1.
2. MVC
3. WCF
4. SQL
基本概念(从运⾏时,编译处理和编程思路解释,注意项)
好吧,我个⼈倾向于把看成是⼀个运⾏时平台,我们写的C#代码通过编译器⽣成托管模块,它包括标准Windows PE⽂件头, CLR头,元数据和中间语⾔代码。运⾏托管的可执⾏⽂件会创建Windows进程然后加载MSCoreEE.dll这个⽂件(垫⽚)。这个进程的主线程会调⽤MSCoreEE.dll的⼀个⽅法初始化CLR(默认AppDomain和托管堆等),(加载)托管的可执⾏⽂件程序集然后调⽤Main⼊⼝⽅法。这样应⽤程序就启动了。在执⾏⽅法之前JIT编译器会把该IL⽅法转换成本地CPU指令并存储到⼀块动态分配的内存块中,把没有构造出来的类型初始化静态变量,调⽤类型构造器构造到AppDomain的加载堆中(使
⽤了互斥线程同步锁保证Appdomain只有⼀个类型),然后执⾏,如果遇到newobj,ldstr,newarr,box等IL指令就在GC堆上构造实例并返回地址。(注意JIT会优化本地代码)(本地代码⽣成器会把IL代码编译成本地代码并存放在⽤户的C:\Windows\assembly⽂件夹下,缺点静态字段只能间接访问,⽆法预测类构造器的顺序)
{接下来聊聊CLR,IL和元数据,CLR是⼀个公共语⾔运⾏时,全称是Common Language Runtime,是⼀个可有多种编程语⾳使⽤的运⾏时,它的核⼼功能有内存管理,程序集加载,安全性,异常处理和线程同步。
元数据由定义表,引⽤表和清单表三类表构成。定义表包括模块定义,类型定义(基类型,public等标志及对该类型下的⽅法,字段,属性和事件的索引),⽅法定义(含private标志,IL代码在模块的偏移量及引⽤参数定义的字段),字段定义,参数定义,属性定义和事件定义。引⽤元数据表包括程序集引⽤,模块引⽤(引⽤其他PE模块的类型时进⾏绑定),类型引⽤(指向类型实现的程序集引⽤,模块引⽤类型引⽤或者模块定义)和成员引⽤。}
AppDomain是⼀个静态概念,⽤来限定对象边界,隔离应⽤程序域。⼀个进程可以拥有多个AppDomain, 每个AppDomain拥有⾃⼰的线程池,线程池默认的线程数量是CPU的个数。如果线程要跨边界访问,必须继承MarshalByRefObect类(每个AppDomain有⾃⼰的加载堆存放类型对象,所以
静态变量也是隔离的)。它可以单独独⽴运⾏,停⽌,有⾃⼰默认的异常处理,不会影响其他AppDomain,可以实现动态卸载DLL(Assembly.LoadFile可以实现动态加载DLL,但由垃圾回收器卸载DLL),在卸载⼀个AppDomain的时候会强制执⾏⼀次垃圾回收。CLR初始化时加载的程序集,⽐如MSCorLib.dll,CLR会创建特殊的Loader堆(加载堆,⽤于存储元数据相关的⽅法表等信息,不受GC控制)使所有类型对象被进程中所以APPDomain共享。
1. 理解CLR,IL和元数据(本地代码⽣成器,与⾮托管代码交互)
2. 共享程序集与强命名
3. 类型,引⽤类型值类型在元数据表和IL的表⽰(装箱和拆箱,可空值类型)
4. 类和实例在元数据表的位置及内存表⽰(类构造器和实例构造器)
5. 静态变量,静态类,扩展⽅法
6. 参数个数及传引⽤和值类型,参数规则
7. 接⼝和基类
8. 泛型
9. 数组和索引器
10. 委托,委托链及实现
11. ⾃定义attribute
12. utility
13. 异常
14. 内存管理,垃圾回收
15. 进程,APPDomain,线程关系
16. 反射及序列化
17. 创建⼀个线程的代价,线程池
18. 异步
19. 基元同步及锁,
20. 混合线程同步
21. OOP 思想及关键字
***类型,值类型与引⽤类型***
在C#中的类型包括int32,int64,string,char等,结构图,枚举类型属于值类型,string, 类(object,委托,数组也是类),接⼝,委托等属于引⽤类型。JIT编译器经过特殊处理将值类型在线程栈上分配,引⽤类型在托管堆上分配(GC堆和⼤对象堆),值类型转换到引⽤类型的时候叫装箱,读取已装箱的值类型称为拆箱(不包括赋值部分,IL代码游box和unbox标记),可空类型其实是⼀个加⼊了⼀个boolean变量的结构体来保存值类型是不是空(num??1:-1,int?)
***Checked***
Byte b = 100;b=Checked((Byte)b+200); //抛出溢出异常
***类和实例***
类是构造在Appdomain的加载堆中的,类构造器不会调⽤基类构造器,主要职责就是初始化类的静态变量,(object的类构造函数直接返回空)所以派⽣类以此构造,若有静态变量,会直接赋值或者在类
构造器⾥重新设置静态变量。实例构造器⼀般是构造在GC堆上,如果对象⽐较⼤,⽐如申请的数组空间⼤于85000字节的话,会分配到⼤对象堆中(GC的第⼆代)。新建对象的时候会初始化类型对象指针使其指向加载堆上的类型对线,初始化同步块索引(线程同步)和实例字段再调⽤类型的构造器。雷构造器只有⼀个,⽽且是静态不公开的,不能带参数(给静态变量赋值)。实例构造器通常是共有的,可有有多个,可带参数。
***静态变量,静态类和扩展⽅法***
在类型实例化之前会初始化静态变量,在类型构造器中可再次更新静态变量,因为它和类型对象在加载堆中,⽽⼀个加载堆在⼀个Appdomain中,⼀个Appdomain中有⼀个线程池,线程池的默认的数量是CPU的个数。所以在多线程访问静态变量的时候就需要线程同步。值类型是在线程栈上的,引⽤类型是在托管堆⽣成对线放回指针,所以他们是线程内所有的。静态类是那些永远不需要实例化的类,⽐如Console,Math等可以声明为静态类。扩展⽅法必须在⾮泛型的静态类中声明,第⼀个参数必须⽤this关键字标记,⽽且是扩展的类型和变量。因此它是构造在加载堆中的,并在使⽤中加载该命名空间,调⽤该静态⽅法时使⽤call,不会对调⽤进⾏空值检查。(委托调⽤)
***call和callvirt***
call可以调⽤静态⽅法,实例⽅法和虚⽅法(经常⽤于⾮虚的⽅法调⽤⼀个虚⽅法)。callvirt不能调⽤
静态⽅法,多态⽅式调⽤虚⽅法。(静态⽅法使⽤call,实例⽅法使⽤callvirt,它会检测实例对象指针是否为空,若是抛出空引⽤异常)
***参数***
使⽤parames可向⽅法传递可变参数,值类型实例⼤于16字节不建议作为参数和返回值。out和ref都是传递指向⼀个实例的指针,但ref要求传递⼀个已初始化的值。若没有这些标记,将会复制⼀个新对象实例。参数和返回值尽量是最弱类型,最好是接⼝⽽不是基类
***接⼝和基类***
接⼝是对边界可以做什么的⼀种集合定义,基类是内部继承的⼀种思想。在C#⾥⾯,派⽣类只能继承⼀个基类,但可以继承多个接⼝,接⼝定义的⽅法,派⽣类⼀定要实现,但基类就不⼀定0。C#编译器不允许抽象类实例化,但它在导⼊堆有类型对象,有类构造函数。C#编译器不允许接⼝有类型构造函数,但它也是在导⼊堆中有类型实例。接⼝可⽤于泛型约束,基类也可以。CLR中抽象类可以有实例,但C#编译器不允许这样做。
***泛型***
在使⽤泛型的时候可以重⽤函数体和成员,避免值类型的装箱拆箱. 泛型和接⼝约束(where T: class)
.
***数组和索引器***
数组是继承Array的,所以它是⼀个引⽤类型,通过索引器来访问具体元素
***委托***
委托类似⼀个函数指针,或者理解为函数名变量。通过相同的参数和返回值可以调⽤不同函数明的函数体。在⽤deletate声明的函数后,会⽣成⼀个委托类,这个委托类继承MulticaseDeletage和Deletage类。委托类有委托变量类型,同步调⽤和异步调⽤。MulticaseDeletage含有_target和_methodPtr(通过元数据表的⽅法定义和成员引⽤到token)和委托链指针三个私有变量,MulticaseDeletage的基类Deletage有target和methodPtr的公有变量,tartget指向对象或者为null(静态类型), methodPtr通过_methodPtr指向的函数地址经过放射构造出methodInfo对象并将地址存放在methodPtr,methodPtr指向委托链上的对象。C#编译器提供匿名⽅法可以不显⽰定义委托,但实际上编译器会知道这是⼀个委托并⽣成委托类。有时候我们使⽤拉姆达表达式(简单的处理)就省去了函数变量明,使⽤=>传递参数,如果代码简单,第⼀条代码可以返回。参数可以是基类(协变性)和返回值可以是派⽣类(逆变性)
***attribute***
attribute其实是⼀个类,它实例到导⼊堆供标记的类型及成员使⽤,并将参数序列化到元数据表中,在反系列成实例标记。
***反射和序列化***
我们有元素据,可以通过反射机制知道别的程序集⾥⾯的类型对象。
***异常***
和windows异常差不多,基于栈
***内存管理***
⼀个进程含有⼀个垃圾回收堆,⼀个进程可以含有多个appdomain,每个Appdomain含有⼀个导⼊堆和⼀个线程池。在线程上实例化的引⽤对象在垃圾回收堆上分配内存并返回引⽤到该线程栈上(值类型直接在线程栈上,因此不需要垃圾回收)。通常把⼩对象标记为0代对象。如果0代对线的空间超过了256K的内存空间的话,会执⾏⼀次垃圾回收,把没有在所有线程栈引⽤的对线调⽤fainlize⽅法进⾏释放,并压缩空间把剩余的对线标记为第1代。如果第1代对线分配的空间⼤于2M,则清理第1代垃圾对象并压缩标记为第2代。通常GC给第2代对象分配10M空间。但GC也会根据操作系统情况,垃圾回收的频繁情况动态调整代的容量⼤⼩。对于超过85000字节的对象,通常分配在⼀个特殊的⼤对象堆
中并认为是GC的第2代。每次垃圾回收启动的时候都会劫持其他线程。下载Appdomain的时候也会强制执⾏⼀次垃圾回收。在程序推出的时候会释放GC堆和所有导⼊堆。
***进程,多线程,Appdomain,线程池,锁,异步***
每个进程多有相互独⽴的虚拟地址,因此进程间不会相互⼲扰。线程就像⼀个虚拟的CPU,⼀个进程有⼀个主线程,每次创建线程都要初始化执⾏上下⽂,分配1M的线程栈,载⼊相应的动态连接库。因为创建线程代价昂贵,AppDomain提供了线程池。它默认的线程个数为CPU 的个数。在使⽤线程池的线程的时候只要取空闲线程,如果满了的话,会在线程池⾥⾯创建新的线程。如果创建的线程空闲的太多,会⾃动销毁空暇线程。通常windows会给每个线程30ms的时间⽚,给每个线程分配优先级。所以线程数量越多,优先级越低就越不能得到CPU资源。即使线程挂起不占⽤CPU资源,也要占⽤内存(CPU指令是基于栈操作的)。因此在调⽤长时间的函数体的时候,如果还占⽤线程将是⼀种浪费。特别是当多个调⽤都在这个长调⽤(IO操作慢)的时候,突然都返回会对内存和CPU造成很⼤的压⼒。在这个情况下可以改⽤异步调⽤,当执⾏IO等长调⽤的时候,把回调函数封送给IO对象等,然后结束当前线程。在IO等对线执⾏完毕后调⽤回调函数在线程池重新拿⼀个空闲线程继续执⾏从⽽达到可伸缩的⽬的。在多线程的时候,静态变量是在导⼊堆中,因此多线程访问该静态变量会有线程安全问题。如果实例单例对线,const 是在编译时确定变量并在之后不能修改,readonly在运⾏时构造函数时确定并之后不能修改(线程同步锁)。可以⽤readonly和static来实例化单例对象。使⽤引⽤类型的
同步索引块来加锁。
***线程同步锁,线程操作,线程优先级***
lock 关键字可以⽤来确保代码块完成运⾏,⽽不会被其他线程中断。它可以把⼀段代码定义为互斥段(critical section),互斥段在⼀个时刻内只允许⼀个线程进⼊执⾏,⽽其他线程必须等待(挂起,⾃旋锁仍然会使⽤时间⽚)。这是通过在代码块运⾏期间为给定对象获取互斥锁来实现的。
创建线程: Thread workThread = new Thread(new ThreadStart(CalcSum)); 线程⼊⼝:void CalcSum();启动: workThread.Start(); 挂起:workThread.Suspend();恢复:workThread.Resume();终⽌:workThread.Abort();休眠:Thread.Sleep(10000);等待线程结束:workThread.Join();
***关键字***
abstract,override, virtual, new
【abstract类可以有实例(msdn.microsoft/zh-cn/hodinfo(v=vs.110).aspx)待搞明⽩】
new 定义⼀个新⽅法,
override重写基类带virtual, abstract, override关键字的⽅法,不能更改virtual⽅法的可访问性。
abstract只能⽤于abstract类中,⽤在⽅法中只有声明没有实现,派⽣类只能⽤override实现,也可以修饰属性,索引器和事件,不能⽤static, sealed修饰符修饰抽象类, 不能⽤override修饰abstract⽅法
virtual可以定义虚⽅法,虚属性,不能与 static、abstract, private 或 override 修饰符⼀起使⽤
const,readonly,static
public, sealed, private
array,list
都是数组对象,但array的数组⼤⼩不能更改,list可以动态改变数组的⼤⼩
类构造器和实例构造器
***接⼝设计***
***类的设计原则***
单⼀责任原则
接⼝隔了原则
开放封闭原则
依赖倒置原则(抽象)
Liskov替换原则
*****************
MVC基础知识
1. MVC架构(惯例优先原则)
2. 路由及配置
3. 视图传值⽅式
4. Web From与MVC区别(VIew)
5. 验证(前段后端验证)
6. 类设计原则
***项⽬模板***
空模板和基本模板,互联⽹应⽤模板对空模板进⾏扩展,加⼊了默认控制器和账户控制器。以太⽹,移动应⽤程序模板(包含了JQuery Mobile), Web API
***路由***
ASP路由只是⼀个模式匹配系统, {controller}/{action}/{id}. 扩展⽤routes.MapRoute(...new{}); (不使⽤ “/”,“~/”)。项⽬中使⽤AngularJS的路由,需要定义view, 同时必须定义对应的contraller.其实那些controller什么也没⼲,所有通信都是通过API,但是还是要实现。可改进。
***页⾯传值⽅式***
ViewBag、ViewData、TempData之间的差别:
ViewData与ViewBag之间⾮常相似,两者使⽤的场景基本⼀致,但是ViewData的类型是很明确的,使⽤的时候经常需要强制类型转换,⽽ViewBag的类型是动态的,不确定的,直接就可以使⽤,他们的传值范围是:controller向view传值,view⾃⼰和⾃⼰传值。⽽TempData存在的⽬的就是为了防⽌redirect时候数据的丢失,所以它的传值范围是当前controller和跳转后的controller之间。
***控制器操作返回类型***
content(): ⽂本;File():⽂件;HttpNotFound(): 404HTTP姿态码;JavaScript():JavssScript的内容;Json():Json数据格式; PartialView():分部视图;Redirect(): 302跳转;RedirectToAction()和RedirectToRoute();View()
***操作参数***
Requset["*"]
模型绑定 public ActionResult Create(string id, DateTime startTime)
复杂对象 public ActionResult Create(Auction auction)
Httpcontext.Body
***验证***
利⽤AuthorizeAttribute标记属性操作,未通过验证的代码会跳到AccountController的Login⾥⾯;
***安全***
默认情况下,ASP.NET MVC不允许对Get⽅式的HTTP请求返回Josn数据,这样可以避免潜在的Josn劫持风险(利⽤JavaScript<script>标记的⽅式,如果请求中的数据包括⼀个JSON数组,则可以导致公开敏感信息)。对⾮敏感信息可通过JsonRequestBehavior.AllowGet来设置。
***⾃定义操作过滤器***
操作过滤器例⼦:
public class MutilpleResponseFormatsAttribute:ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var request = filterContext.HttpContext.Request;
var viewResult = filterContext.Result as ViewResult;
if(viewResult == null) return;
if(request.IsAjaxResquest())
{
filterContext.Result = new PartialViewResult{ TempData = viewResult.TempData, ViewData = viewResult.ViewData,ViewName = viewResult.ViewName,};}
else if(Requst.IsJsonRequst())
{filterContext.Result = new JsonResult{Data = viewResult.Model};}
}
}
***数据发⽣***
默认的Json模型绑定限制只包含Json格式,不允许部分字段是。复杂Json对象可以使⽤JsonModelBinder,模型绑定器分别处理每个属性,所以可以混⽤不同字段,部分字段⽀持Json数据,也可以不⽀持。
Public class JsonModelBinder:DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext binding Context)
{
string json = string.Empty;
var provider = bindingContext.ValueProvider;
var providerValue = provider.GetValue(bindingContext.ModelName);
if(providerValue != null) json = providerValue.AttemptedVaule;
if(Regex.IsMatch(json,@"^(\[.*\]|{.*})$“))
{return new JavaScriptSerializer().Deserialize(json,bindingContext.ModelType);}
return base.BindMode(controllerContext, bindingContext);
}
}
替换默认绑定器
protected void Application_Start()
{
ModelBinders.Binders.defaultBinder = new JsonModelBinder();
}
使⽤绑定器
public class CreateProductRequest
{
/// ...
[Required]
[JsonModelBinder]
public IEnumerable<CurrencyRequest>UnitPrice{get;set;}
}
***Web API路由注册***
routes.MapHttpRoute(
name:"DefaultApi",
routeTemplate:"api/{controller}/{id}",
defaults:new{id=RouteParameter.Optiona;}
);
***OData***
开放数据协议使⽤URL参数表达式来⽀持数据分页和过滤(/api/Auction?$top=3&$orderby=CurrentByid).⽀持查询参数有$filter, $orderby, $skip, $top,必须返回IQueryable<T>类型的结果(或者转换 AsQueryable())。
public IQueryable<Auction> Get()
{
return _repository.All<Auction>().AsQueryable();
}
***异常过滤器***
一个线程可以包含多个进程异常过滤器可以处理控制器⾥⾯的⾮HttpResponseException类型异常。继承System.Web.Http.Filters.IExceptionFilter接⼝或者ExceptionFilterAttribute, 重写OnException()。
using System.Diagnostics;
using System.Web.Http.Filters;
public class CustomExceptionFilter:ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if(context.Response == null) context.Response = new HttpResponseMessage();
context.Response.StatusCode = HttpStatusCode.NotImplemented;
context.Response.Content = new StringContent("Custom Message");
base.OnException(context);
}
}
创建完后有两种注册⽅式注册才能⽤。通过GlobalConfiguration.Configuration.Filters集合注册全局异
常过滤器(处理除了httpResponseException之外的所有注册异常)。在web API的控制器⽅法上直接标记。
static void ConfigureApi(HttpConfigurationconfig)
{
config.Filters.Add(new CustomExceptionFilter());
}
protected void Application_Start()
{
ConfigureApi(GlobalConfiguration.Configuration);
}
[CustomExceptionFilter]
public Auction Get(string id)
{
var result = _repository.Single<Auction>(id);
if(result == null) throw new Exception("Item not Found!");
return result;
}
***repository对象及依赖注⼊***

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。