声明
本文欢迎转载,原文地址:http://www.cnblogs.com/DjlNet/p/7192354.html
前言
这些知识你还记得么吗,\(^∀^)メ
误区:对象在C#中默认是通过引用传递的
void AppendHello(StringBuilder builder)
{
builder.Append("Hello");
}
调用这个方法时,参数值(对StringBuilder的一个引用)是以值传递的方式传递的。如果想在方法内部更改builder的变量值,如:builder=null;
对调用者来说是看不见的。
理解:JIT编译器如何处理泛型
对于所有的封闭类型,JIT的职责就是将泛型类型的IL转换为本地代码,我们以List作为例子,首先JIT为每个以值类型作为类型实参的封闭类型都创建不同的代码,理论上对于一些值类型来说,代码是可以共享的,但是JIT必须十分谨慎,不仅需要考虑空间大小的问题,还要考虑垃圾回收的问题,所以第一次创建独享的代码。那么,所有使用引用类型(string、stream、stringbuilder等)作为类型实参的封闭类型都共享相同的本地代码,之所以可以这样做,是由于所有引用都具有相同的大小(32位CLR上是4个字节,64位CLR是8个字节,在任何一个特定的CLR中所有引用具有相同的大小)。关于泛型的新增API,GetGenericTypeDefinition作用于已构造的类型,获取它的泛型类型定义和MarkGenericType作用于泛型类型的定义,返回一个已构造类型,诸如此类还有:GetGenericArguments、IsGenericTypeDefinition、IsGenericType、MarkGenericMethod、IsGenericMethod等等。
Nullable类型理解与使用null进行赋值和比较原理
注意Nullable是一个结构也就是值类型,对于例如Nullable的变量来说,直接包含了一个bool和一个int成员,而不是其他对象的引用。关于null赋值比较,原理:C#编译器允许使用null在比较和赋值时表示一个可空类型的空值。其中这样来处理的原因,相信也是从语言层面让语义更加符合自然逻辑,获得和引用类型null的同样体验。那么到底编译器关于可空值类型帮我们做了什么呐,int? a=null; if(a==null){ .... }
,与null比较其实在编译器生成IL代码中,被转换为调用a.HasValue
,对a赋值为null,其实也是调用了Nullable的构造函数创建一个空值实例而已,注意直接调用a.Value在没有真正的值提供时将会抛出异常。
注意匿名函数变量捕获
划重点: 1、捕获的是变量本身,而不是创建委托实例时它的值 2、捕获的变量的生存周期被延长了,至少和捕获它的委托一样长 3、多个委托可以捕获同一个变量 4、在循环内部,同一个变量声明实际上会引用不同的变量“实例”(这点在R#也会提示开发者) 5、如果捕获的变量不会发生改变,就不需要担心 6、在C#5或者以上修正foreach的表达含义,但是for依然需要注意。
理解Yield的工作流程和成为奠定异步Async/Await的设计基石
这里直接引用书中的代码加强记忆和再次熟悉下流程,如下图所示代码: 关于代码的执行流程那得自个儿看看,慢慢跟着执行流程,相信就能明白Yield在代码执行中起到的作用,就貌似能在不同的方法之间跳跃,总的来说可以归为几点: 1、在第一次调用MoveNext之前,CreateEnumerable中的代码不会被调用 2、所有工作在调用MoveNext时就完成了,获取Current的值不会执行任何代码 3、在yield return的位置,代码会停止执行,方法暂时返回调用者方法,在下一次执行MoveNext时有继续在下一行代码继续执行 4、在一个方法中的不同位置可以编写多个yield return语句 5、代码不会在最后的yield return处结束而是通过返回false的MoveNext调用来结束方法的执行 上面说了这么多,在看到第3和4点的时候有没有感觉到await的部分作用似曾相识,await也可以让方法返回而且可以等在那里等到结果拿到之后接着那个“断点”继续执行,而且在一个Async标记的异步方法中可以,有多个await的拆包操作,所以在一定程度上面yield在思想和设计层面上,给后面的C#5的异步埋下了铺垫。从状态机的角度来看(这块并不是很熟悉,所以说错了,请斧正)在C#5中为迭代器块而生成的状态机和那些异步函数而生成的状态机之间的相似性是惊人的。异步开发中两个复杂的问题就是:1、处理状态 2、在感兴趣的事情发生之前进行有效的暂停,迭代器使得这两个问题得以完美的解决。 这里提供一下yield实现异步的伪代码,也是书中的代码片段:(伪代码是基于CCR实现的,主要是明白它想传递表达的意思,即可代码部分看看就好)
static IEnumerator<ITask> ComputeTotalStockVal.(str.user,str.pass)
{
string token=null;
yield return Arbiter.Receive(false,AuthService.CcrCheck(user,pass),
delegate(string t){ token=t; });
IEnumerable<Holding> stocks=null;
IDictionary<string,decimal> rates=null;
yield return Arbiter.JoindReceive(false,
DbService.CcrGetStockHoldings(token),
StockService.CcrGetRate(token),
delegate(IEnumerable<Holding> s,IDictionary<string,decimal> r)
{
stocks=s;
rates=r;
});
OnRequestComplete(ComputeTotal(stocks,rates));
}
大致对上面代码做个解释说明虽然是伪代码,CCR对我们的代码进行了调用也就是调用MoveNext,第一个yield return才会执行,AuthService里面的CcrCheck方法启动一个异步请求,CCR会等待直到它完成,然后提供给它的委托来处理拿到的结果也就是token,然后接着再次调用MoveNext,方法接着执行,JoindReceive并行启动两个异步请求,在两个请求都完成的情况下,利用后续的委托去处理结果,在这之后MoveNext再次调用,就完成了全部的请求处理了。
小总结
再回看一些基础知识的时候,才发觉原来语言层面的设计不比一些高层架构的设计的重要性和观赏性差(虽然没什么可比性,哈哈),好了,天不早了,后面应该会出续篇,等这个完了之后,应该会开始对一些大家关注的点出发,脚踏实地的从代码、需求和设计层面考虑去写一些可使用的代码片段或者程序设计,用作以后参考或者借鉴。最最后,好好给自己一记耳光,咋就做人真懒呐,看看离上次发文都多长时间了,送给自己的话:从现在起做一个不那么懒的人吧!谨记!!!