技术掘金 - 2013年10月
type: channel
link: http://blog.guqiankun.com/2013/10/
description: 技术掘金,专注于Web互联网开发,分享技术,致力于打造好的技术博客
items: 《渝李文集 -- 静日书》
items: Android网络监控抓包工具的制作(tcpdump的使用)
items: 《渝李文集静日书 -- 江湖这么大》
items: 创建基于二进制的组件
items: Effective C# 原则29：仅在对基类进行强制更新时才使用new修饰符
items: Effective C# 原则28：避免转换操作
items: 《渝李文集静日书 -- 失落的绿洲》
items: Effective C# 原则27：避免使用ICloneable
items: JQuery操作html Table的一些好方法
items: Effective C# 原则26：用IComparable和IComparer实现对象的顺序关系

《渝李文集 -- 静日书》
type: item
date: 2013-10-24T07:00:00+08:00
description: 《渝李文集静日书 -- 赤水莲花》《渝李文集静日书 -- 蜡手馆的来客》《渝李文集静日书 -- 留守风花树洞的冬天》《渝李文集静日书 -- 留一颗掌心朱砂》《渝李文集静日书 -- 破天》《渝李文集静日书 -- 每一只鼹鼠都能恋爱》《渝李文集静日书 -- 一场游戏》《渝李文集静日书 -- 秦淮河的风月桃花》《渝李文集静日书 -- 烫暖爱情的牛肉汤》《渝李文集静日书 -- 悟空有没有爱过谁》《渝李文集静日书 -- 完美爱情童话》《渝李文集静日书 -- 双输戏》《渝李文集静日书 -- 夜奔》《渝李文集静日书 -- 七步成殇》《渝李文集静日书 -- 和幸福一起痛》《渝李文集静日书 -- 倾城》《渝李文集静日书 -- 日光碎片》《渝李文集静日书 -- 丢失的U字》《渝李文集静日书 -- 关于一棵菠菜的等待》《渝李文集静日书 -- 那些花儿老去》《渝李文集静日书 -- 一枚硬币背上的幸福》《渝李文集静日书 -- 宋恩南的夏日薄荷香》《渝李文集静日书 -- 那一年天使离去》《渝李文集静日书 -- 惊航》《渝李文集静日书 -- 寻找书童马自强》《渝李文集静日书 -- 失落的绿洲》《渝李文集静日书 -- 江湖这么大》

Android网络监控抓包工具的制作(tcpdump的使用)
type: item
date: 2013-10-23T07:00:00+08:00
description: 最近做一个Android联网抓包的工具，自己在网上搜索了好久，发现还是没有头绪，于是考虑在linux层上下功夫于是采用linux的tcpdump来实现了抓包的功能，用简单的话来定义tcpdump，就是：dumpthe traffic on anetwork，根据使用者的定义对网络上的数据包进行截获的包分析工具。作为互联网上经典的的系统管理员必备工具，tcpdump以其强大的功能，灵活的截取策略，成为每个高级的系统管理员分析网络，排查问题等所必备的东东之一。对于tcpdump详细的信息可以参考百度百科关于TCPDUMP的介绍我这里主要介绍一下如何在Android上使用tcpdump，我所采用的测试机是破解版G2和模拟器，具体的就是在APK程序里执行linux命令，tcpdump -p -vv -s0 -w /sdcard/capture.pcap 这样就能够将抓到的包在sdcard上写一个capture. pcap文件，而这个文件可以用wireshark在PC上打开进行查看，非常方便。具体的源代码如下，MainHook.javapackage zy.hook; import java.io.IOException; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class MainHook extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); try { //Runtime.getRuntime().exec("su"); //Runtime.getRuntime().exec("dd if=/sdcard/tcpdump of=/data/local/tcpdump"); //Runtime.getRuntime().exec("chmod 6755 /data/local/tcpdump"); Runtime.getRuntime().exec("tcpdump -p -vv -s 0 -w /sdcard/capture.pcap"); Log.i("run","success!!!!!!!!!"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.i("run",e.toString()); } } } AndroidManifest.xml<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="zy.hook" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainHook" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="7" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest> 尤其不要忘记了权限的声明，运行程序我们就可以抓包了会在sdcard上生成capture.pcap文件，抓一段时间之后，将文件从sdcard上导出，然后我们用wireshark打开 可以非常方便的查看我们抓到的包

《渝李文集静日书 -- 江湖这么大》
type: item
date: 2013-10-17T07:29:00+08:00
description: 发布于：2007-10-19 21:37:031我接到梨落的竹筒信时，正在明月楼上搂着姑娘喝小酒。红衣的姑娘笑起来嗓门挺大，酒量也很好，我喝得有点头晕了，她还没醉，两只红艳艳的手指夹起一颗葡萄喂到我嘴边，唤，二公子……这时窗外有鸽子扑翅的声音，我探身看了看，果真看到傅家堡特有的蓝灰色信鸽。解开它脚上的信筒，里面有个白色小布卷，匝三道红线，看来是加急信。蘸点口水拆信，方才的姑娘从背后挤过来。我瞥了一眼信的内容，心里一咯噔，转身就把她给甩开了。这种时刻我可顾不上怜香惜玉，罩上袍子，蹬了马靴，一阵风似的朝马厩冲去。那封信还在我手里，细软的白布条上有泪痕，是梨落的泪，不到万不得已她不会写信给我，尽管只是简短的一行字，却硌得我的心像是要碎掉。信鸽冲天而去时我的马也在飞奔，游龙剑在腰间轻鸣。我心里除了愤怒还有担忧，甚至于，更深的悔恨也狠狠攫住了我。我不明白，大哥在得到这样一个绝世佳人之后，为什么还会陋巷寻花长街问柳，难道说堂堂傅家堡堡主夫人，那曾撩动无数江湖英雄竞折腰的美人毕梨落，会失色于烟视媚行的青楼女？而富可敌国的傅家堡里，那日间篱下饮酒，晚来红帐温香的生活，还不足以令大哥收敛心性，不足以令他待她，有多一点温情、多一点真心？当初大哥是极爱她的，甚至不顾性命之虞，调全堡上下七百多号习武之人，从天灵山上力克众人夺梨落回来。那场婚宴，在我记忆中是如此盛大欢喜，从傅家堡主城正厅铺出的红毯，穿城绕林，一直绵延到堡外三十里，即便帝王之家送迎嫁娶，那排场也不外如此。可是，梨落的信里却哀哀写道：兄纳妾在即，嫂望速归。嫂望速归，这四个字是一道魔咒，这称呼，比我腰间的游龙剑更锋利。悔恨铺天盖地袭来，我知，终其一生，无论如何，毕梨落是我心间难以拔除的一根玫瑰刺。2回到堡内大哥不在，正厅里只有管家廖文在等我。他走过来作了个揖说，二公子，夫人有请。整个傅家堡的气氛还是如常压抑，自梨落有孕搬出中房以来，大哥耐不住寂寞，整日里不是去坊间喝花酒，就是带明月楼的姑娘回来，在西侧倚翠居里厮混。为免梨落伤心，我交代下人不准多言妄行，由此独居东边玉箫阁的梨落少见少闻了许多和大哥有关的荒唐事，只是堡内从此也便寂寥下去。廖文带我去玉箫阁，一路上他回了几次头，我知他有话要问我，果然，他在廊外花间停下来，指指楼上，二公子，你打算如何？他指的是梨落。我犹疑片刻，坦白地对他说，阿文，我无能为力。廖文看了我一眼问，你不打算带她走？我心里揣满了悲伤与无奈，我说整个江湖都是人，人人都有一张嘴，我与梨落，要走到哪里去呢？廖文叹口气说，担着携嫂私奔的罪名，傅家堡二公子怕也确是难在江湖立足。我顿了顿说，长宁毕竟是我大哥，前事我已然对他不住，如今再不可令他难堪。这番话，廖文是明白的，在傅家堡里，我从未把他当做外人。那年梨落进傅家门，喜堂之上，风吹喜帕的瞬间，我便深知那双波光潋滟的眼能带给我怎样的劫。那夜我平生第一次大醉，是廖文扶我回房。那时他还不是傅家堡总管，只是个刚进堡的普通下人，所有人都在为大哥和梨落欢庆祝福，只有他注意到已醉得不省人事的我，整夜待在我房里，陪我至天明，并听我叫了一整夜梨落的名。我不拿他当外人，升他做管家，只因这波涛暗涌的宏城巨堡内，我需要有这么一个人来懂我。我曾深深追悔，原本天灵山一战，我与大哥打过赌，不管生死都要拿下。那场大战的赌注有两个，一个是绝世美人毕梨落，另一个，是我腰上这把游龙软剑。那一战，我们败了江南三杰，降服漠北双煞，削了崆峒弟子的鼻子，断了唐门女掌门的手臂，还送久负盛名的武林第一庄——凌霄山庄庄主夫妇一并归西。华山、青城、武当通通拜于我们脚下，最后是少林是空大师，他不为美人而来，却为游龙软剑——这把传言中能守城护主的绝世名剑，是可以统领江湖号令群雄的千年至宝，就算是空，也不免心动。而我悔的，正是战胜之后我与大哥的选择。我得到了游龙软剑，他得到了毕梨落。那一天的风很大，我们站在高高的天灵山顶上，他抱着白狐软裘里没有露出面容的梨落，而我，还在为腰间的游龙剑喜不自胜。那时我还不曾这样刻骨铭心地爱过一个人，还不曾懂得，绕指柔比金刚圈更易将一个人的心禁锢深锁。3梨落比我出堡之前更瘦了，我方推门而入，她已经把自己紧紧嵌在我怀里。我极艰难地叫了一声大嫂，她一颤，随后抬头望着我，眼眸里是一片荒凉。她说你还要我怎样？同意他纳妾？眼睁睁看着一个烟花女进堡，而后为他生养子嗣，将我与孩子一步步排挤出去？我办不到，死也不同意。我还是抱住了她。我说你不要胡思乱想，你是大哥的正妻，孩子是大哥的长子，在傅家堡里，你永远都是当家主母，任何人，任何事，都不比你对大哥来得重要。梨落退开，眼泪从她面颊上狠狠砸下来。她摇头说，可是长风，这是你的孩子啊。竟是如此。这比大哥纳妾的消息更令我震惊，我的脑子似要炸开。我想起很久以前那段狂乱的耽溺于鱼水之欢的日子。那时梨落与大哥也不过新婚，却终日以泪洗面，大哥追问…

创建基于二进制的组件
type: item
date: 2013-10-16T07:29:00+08:00
description: 随着类的数量增加，创建二进制的组件就变得很平常了：你想分离部分功能。所有不同的是，二进制组件可以让你独立的保存这些离散的功能。创建的组件程序集可以方便的共享逻辑，方便交叉语言编程，以及方便布置。在.Net程序就是组件包，每一个程序什么样可以随时更新和发布。你应该已经知道，基于程序集之间的应用程序是多么容易更新呀，程序集之间的偶合是多么好呀!最小偶合就是说更多的是减少了程序集之间复杂的依懒关系。同时也意味着你可以简单的更新小块新的程序集。这一章就是关于创建简单易用，容易布置，以及容易更新的程序集。.Net环境中的应用程序可以由多样的二进制组件组成。随后，你可以独立的更新这些组件，也就是可以在一个多程序集应用程序中安装与更新一个程序集。但你必须明白一件事，那就是CLR是如何发现和载入这些程序集的。在你创建这些二进制组件时，你也就必须创建符合这些明确期望的组件。接下来就介绍这一概念。CLR并不会在程序启动时加载全部的引用程序集。更合适的作法是当运行时须要程序的成员时，加载器才解决程序集的引用问题。这可能是调用一个方法或者访问数据，加载器先查找引用的程序集，然后加载它，然后JIT编译须要的IL。当CLR须要加载一个程序集时，先是断定那个文件要加载。程序集的元数据包含了对所有其它程序集的引用记录，这个记录有强名称和弱名称之分。一个强名称(程序集)由四部分组成：程序名，版本号，语言属性(译注：这里的语言范指区域性特定的信息，如关联的语言、子语言、国家/地区、日历和区域性约定)，以及公开密钥。如果程序集不是一个强名称程序，那么就只记录了程序集名。如果你的程序是一个强名称程序集，那么你的程序集就不太可能被一些恶意的组件(程序集)所取代。强程序集还可以让你用配置文件对组件的版本进行配置，是新的，还是先前的。确定正确的程序集以后，CLR接来就断定这个程序集是否已经在当前的应用程序中加载。如果是，就用原来的。如果不是，CLR就继承查找程序集。如果程序是强名称的，CLR就先在全局程序缓存(GAC)中查找，如果不在GAC中，加载器就检测代码目录(codebasedirectory，译注，这里只是译为代码目录，其实并不是源代码所在的目录)中的配置文件，如果当前代码目录存在，那就是唯一被搜索程的目录了。如果在代码目录中没有找到所要的程序集，那么加载就失败。应用程序目录。也就是与主应用程序集同在的位置。语言目录。这是一个在应用程序目录下的子目录，这个目录与当前语言名匹配。程序集子目录。这是一个与程序集同名的子目录，这两个可以组合成这样：[语言]/[程序集名]私有的运行目录(binPath)。这是一个在应用程序配置文件中定义的私有目录。它同样可以和语言和程序集名组合：[bin目录]/[程序集名], 或者[bin目录]/[语言],还可以是：[bin目录]/[语言]/[程序集名]。从这个讨论中你应该明白三个内容：第一，只有强名称程序集才能放到GAC中。其次，你可以通过配置文件来修改默认的行为，从而升级程序中个别的强名称程序集。第三，强名称程序集可以阻止对程序集的恶意篡改，从而提供更安全的应用程序。了解CLR是如何加载程序集的，有利于在实际操作中考虑如何创建和更新组件。首先，你应该使用强名称程序集，把所有的元数据信息都记录下来。当你在VS.net中创建一个项目时，你应该把assemblyInfo.cs文件中的所有属性都填满，包括完整的版本号。这会让你在后面的升级中更简单的。VS.net在assemblyInfo.cs中创建三个不同的部份，首先就是信息报告：[assembly: AssemblyTitle("My Assembly")] [assembly: AssemblyDescription ("This is the sample assembly")] #if DEBUG [assembly: AssemblyConfiguration("Debug")] #else [assembly: AssemblyConfiguration("Release")] #endif [assembly: AssemblyCompany("My company")] [assembly: AssemblyProduct("It is part of a product")] [assembly: AssemblyCopyright("Insert legal text here.")] [assembly: AssemblyTrademark("More legal text")] [assembly: AssemblyCulture("en-US")] 最后一条，AssemblyCulture只针对本地化程序集。如果你的程序不包含任何的本地化资源，就空着。否则就应该遵从…

Effective C# 原则29：仅在对基类进行强制更新时才使用new修饰符
type: item
date: 2013-10-15T07:19:00+08:00
description: 你可以用new修饰符来重新定义一个从基类中继承来的非虚成员。你可以这样做，但并不意味着需要这样做。重新定义非虚方法会导致方法含意的混乱。如果两个相关的类是继承关系，那么很多开发人员可能会立即假设两段代码块是做完全相同的事情，而且他们也会这么认为：object c = MakeObject( ); // Call through MyClass reference: MyClass cl = c as MyClass; cl.MagicMethod( ); // Call through MyOtherClass reference: MyOtherClass cl2 = c as MyOtherClass; cl2.MagicMethod( ); 一但使用了new修饰符以后，问题就完全不一样了： public class MyClass { public void MagicMethod( ) { // details elided. } } public class MyOtherClass : MyClass { // Redefine MagicMethod for this class. public new void MagicMethod( ) { // details elided } } 这样的实际操作会让很多开发人员迷惑。因为当你在同一个对象上调用相同的函数时，一定希望它们执行同样的代码。但实际上是，一但你用不同的引用来调用同名的函数，它们的行为是不一样的，这感觉非常糟糕。它们是不一致的。一个MyOtherClass类型的对象所表现的行为会因为你引用的方式不一样而有所不同。这就是new修饰符用在非虚成员上的后果。其实这只是让你在类的名字空间中添加了一个不同的方法(虽然它们的函数名是相同的)。非虚方法是静态绑定的，不管哪里的代码，也不管在哪里引用，MyClass.MagicMethod()总是严格的调用类中所定义的函数。并不会在运行时在派生类中查找不同的版本。另一方面，虚函数动态的。运行时会根据不同的类型对象调用不同的版本。建议大家避免使用new修饰符来重新定义非虚函数，这并不要太多的解释，就像推荐大家在定义一个基类时应该用虚方法一样。一个类库的设计者应该按照合某种约设计虚函数。也就表示你期望任何派生类都应该修改虚函数的实现。虚函数的集合就相当于是定义了一个行为的集合，这些行为是希望在派生中重新实现的。设计默认的虚函数就是说派生可以修改类中的所有虚的行为。这确实是说你不想考虑所有派生类可能要修改行为的分歧问题。相反，你可以把时间花在考虑把什么样的方法以及属性设计成多态的。当然，只有它们是虚行为的时候才能这样做。不要考虑这样会限制类的用户。相反，应该认为这是给类型的用户定义行为提供了一个入口向导。有且只有一种情况要使用new修饰符，那就是把类集成到一个已经存在的基类上时，而这个基类中已经使用了存在的方法名，这时就要使用new了(译注：就是说基类与派生类都已经存在了，是后来添加的继承关系，结果在添加继承关系时，发现两个类中使用了同样的方法名，那么就可以在派生类中添加一个new来解决这个问题)。因为有些代码已经依懒于类的方法名，或者已经有其它程序集在使用这个方法。例如你在库中创建了下面的类，使用了在另一个库中定义的BaseWidget：public class MyWidget : BaseWidget { public void DoWidgetThings( ) { // details elided. } } 你完成了你的widget,而且用户可以使用它。然而你却发现BaseWidget公司发布了一个新的版本。而这正是你所渴望的，于是你立即购买并编译你的MyWidget类。结果失败了，因为BaseWidget的家伙们已经添加了他们自己的DoWidgetThings方法：public class BaseWidget { public void DoWidgetThings() { // details elided. } } 这是个难题，你的基类中隐藏了一个方法，而这又是在你的类的名字空间中。有两个方法解决这个问题，一个就是修改你的类中的方法名：public class MyWidget : BaseWidget { public void DoMyWidgetThings( ) { // details elided. } } 或者使用new修饰符：public class MyWidget : BaseWidget { public new void DoWidgetThings( ) { // details elided. } } 如果你可以拿到所有使用MyWidget类的源代码，那么你应该选择修改方法…

Effective C# 原则28：避免转换操作
type: item
date: 2013-10-11T21:11:00+08:00
description: 转换操作是一种等代类型(Substitutability)间操作转换操作。等代类型就是指一个类可以取代另一个类。这可能是件好事：一个派生类的对象可以被它基类的一个对象取代，一个经典的例子就是形状继承。先有一个形状类，然后派生出很多其它的类型：长方形，椭圆形，圆形以及其它。你可以在任何地方用图形状来取代圆形，这就是多态的等代类型。这是正确的，因为圆形就是一个特殊的形状。当你创建一个类时，明确的类型转化是可以自动完成的。正如.Net中类的继承，因为System.Object是所有类型的基类，所以任何类型都可以用System.Obejct来取代。同样的情况，你所创建的任何类型，也应该可以用它所实现的接口来取代，或者用它的基类接口来取代，或者就用基类来取代。不仅如此，C\#语言还支持很多其它的转换。当你为某个类型添加转换操作时，就等于是告诉编译器：你的类型可以被目标类所取代。这可能会引发一些潜在的错误，因为你的类型很可能并不能被目标类型所取代(译注：这里并不是指继承关系上的类型转换，而是C\#语言许可我们的另一种转换，请看后文)。它所的副作用就是修改了目标类型的状态后可能对原类型根本无效。更糟糕的是，如果你的转换产生了临时对象，那么副作用就是你直接修改了临时对象，而且它会永久丢失在垃圾回收器。总之，使用转换操作应该基于编译时的类型对象，而不是运行时的类型对象。用户可能须要对类型进行多样化的强制转换操作，这样的实际操作可能产生不维护的代码。你可以使用转换操作把一个未知类型转化为你的类型，这会更加清楚的表现创建新对象的操作(译注：这样的转换是要创建新对象的)。转换操作会在代码中产生难于发现的问题。假设有这样一种情况，你创建了如图3.1那样的类库结构。椭圆和圆都是从形状类继承下来的，尽管你相信椭圆和圆是相关的，但还是决定保留这样的继承关系。这是因为你不想在继承关系中使用非抽象叶子类，这会在从椭圆类上继承圆类时，有一些不好实现的难题存在。然而，你又意识到每一个圆形应该是一个椭圆，另外某些椭圆也可能是圆形。(译注：这一原则中作者所给出的例子不是很恰当，而且作者也在前面假设了原因，因此请读者不要对这个例子太钻牛角尖，理解作者所在表达的思想就行了，相信在你的C\#开发中可能也会遇到类似的转换问题，只是不太可能从圆形转椭圆。)这将导致你要添加两个转换操作。因为每一个圆形都是一个椭圆，所以要添加隐式转换从一个圆形转换到新的椭圆。隐式转换会在一个类要求转化为另一个类时被调用。对应的，显示转化就是程序员在代码中使用了强制转换操作符。public class Circle : Shape { private PointF _center; private float _radius; public Circle() : this ( PointF.Empty, 0 ) { } public Circle( PointF c, float r ) { _center = c; _radius = r; } public override void Draw() { //... } static public implicit operator Ellipse( Circle c ) { return new Ellipse( c._center, c._center, c._radius, c._radius ); } } 现在你就已经实现了隐式的转换操作，你可以在任何要求椭圆的地方使用圆形。而且这个转换是自动完成的：public double ComputeArea( Ellipse e ) { // return the area of the ellipse. } // call it: Circle c = new Circle( new PointF( 3.0f, 0 ), 5.0f ); ComputeArea( c ); 我只是想用这个例子表达可替代类型：一个圆形已经可以代替一个可椭圆了。ComputeArea函数可以在替代类型上工作。你很幸运，但看下面这个例子：public void Flatten( Ellipse e ) { e.R1 /= 2; e.R2 *= 2; } // call it using a circle: Circle c = new Circle( new PointF ( 3.0f, 0 ), 5.0f ); Flatten( c ); 这是无效的，Flatten()方法要求一个椭圆做为参数，编译器必须以某种方式把圆形转化为椭圆。确实，也已经实现了一个隐式的转换。而且你转换也被调用了，Flatten()方法得到的参数是从你的转换操作中创建的新的椭圆对象。这个临时对象被Flatten()函数修改，而且它很快成为垃圾对象…

《渝李文集静日书 -- 失落的绿洲》
type: item
date: 2013-10-08T07:28:00+08:00
description: 发布于：2007-9-26 20:18:16我只记得那一天晴好。那一天，我的骆驼死了。是正午，太阳很大，它倒下去的时候，发出了一声沉重的哀鸣。尽管我背上的包袱很重，水囊里一滴水也没有了，但我还是决定停下来，为它在原地掘一个坑，令它的尸骨有一处不受烈日曝晒的安息地。神诫，常怀恻隐之心，我是记得的。而只此一念，这个埋尸坑，令我青史留名。忘记说，史，不仅是竹简麻编的中国通史。亦忘记说，名，我的名是，斯文?赫定。王胤我叫王胤，绿林山人。假如你还记得多年前的那场绿林之战，你就会对我的姓肃然起敬。我的祖籍在新市，这样说你可能更为明白，在新市，没有人不知道王姓人家，我的先祖正是王匡和王凤，在外戚王莽当政之际，是他们带领挖野荸荠尚不能为生的农民发动了一场声势浩大的起义，而鏖战两万官军的那场战争，有人称为，绿林之战。这是我先祖积下的功德，他们的侠义精神至今在江湖流传，所有生于草莽仗剑为民的人，都被称为“绿林好汉”，而我亦有幸，沾染祖上无上荣光。我的身份是游侠，这是一个令人欣慰的职业。很多人对游侠的含义有误读，他们认为，游，则为游手好闲；侠，即是惹是生非。他们所能理解的游侠，既有别于官府差官，又不同于义军将士，当然也达不到发个“巨子令”就威震江湖号令群雄的地步。所以游侠是一种被忽视进而被藐视的职业，甚至于，它竟算不上一种“职业”。但祖上有遗训告知后辈，江湖之上，只有游侠一业，得为存，得为寿，得为公。那年是绿林之战的第二年，绿林山上发生了一场罕见的瘟疫。五万绿林军几乎死掉一半，剩下的只好兵分三路，转战汉江两岸。此后刘秀在南阳郡舂陵乡随同起兵，破落贵族刘玄成了更始帝，拜我先祖为上公。但所谓天道大行的起义军，却在不久后刘秀自封大汉光武帝，我先祖与赤眉军将领樊崇一起被杀后，分崩瓦解。王道，是野心家的王道；江湖，是求胜者的江湖。这番道理，我懂得。故此，从我出生之时起，我便在流浪。流浪对于一个游侠来说，就好像空气水分一样不可缺少。我从汉江浪迹至夷地，又从南疆跋涉至塞外，行走，当然是没有意义，假如一定要给上一个，或许是，逃避。逃避一种渴望，我深知。我的内心，潜伏有扬名立万的狂热因子，这与游侠精神是绝对背离的，这也与祖训背离。有时候我在独行的路上，耐不住庞大而漫长的寂寞，就会背一本残破帛书上写的话，那段话记载了我先祖的身后事：身首异，四肢残，烹脑焚心，佐以甘食；又以皮为鼓，骨为槌，隆隆进舞，王尊堂前，顶骨为杯，割腥啖膻，笑若枭鸣……每每背到此处，我的心里，就升起一种难言的悲凉。这悲凉与想要扬名的狂热化为两股气流，在我血液之中逆行冲撞，然后通通汇至左胸，就好像要咆哮而出一样。很多时候我运气打坐，诵武功心法，疑心这就是武艺精进时高手们常说的气通任督二脉，但那股气，一直都没有真正从我左胸喷出，而做为一名游侠，我真正仰赖着行走江湖的，非难驭的无形之气，而是背上那把有形之剑。我的剑，名唤纯钧。当年勾践想用纯钧换得骏马良田和大城，薛烛却说，这把剑是天人共铸的不二之作。为铸这把剑，千年赤堇山山破而出锡，万载若耶江江涸而出铜。铸剑之时，雷公打铁，雨娘淋水，蛟龙捧炉，天帝装炭。铸剑大师欧冶子承天之命呕心沥血，与众神铸磨十载此剑方成。剑成之后，众神归天，赤堇山闭合如初，若耶江波涛再起，欧冶子也力尽神竭而亡。故此，纯钧已成绝唱。纯钧是我祖上留下，在中原腹地，它从未开光。但终于，我还是用它杀过人，在边塞，一队异族人。那日我正在关内一间小店饮酒，酒后，我原打算离开中原，去塞外隐居。这些年走南闯北，令我会得各种异族语言和习俗，塞上牧马，是我能想到的又一种游侠生活。那家店卖关中有名的烧酒，没有名字，只是辛辣醇厚，用红釉瓷瓮盛着，不设碗，就着喙形瓮口饮，甚是爽快。我有好酒量，还有好的品酒本事，尤其是对酒香酒味，能称上过喉不忘。这酒分外特别，令我肠胃妥帖，我便额外叫了半斤牛肉，欢喜大啖。正嚼得欢，嗖的一声，门外酒招飞了进来，断掉的半截竹枝牢牢斜插进我面前剩下的半块牛肉里。我大骇，想不到这边关之地，还有这样的高手。这时候外面的喊杀声已起，我探头朝门外看，正是一队剽悍的异族骑兵，在追杀一个浑身染血的男人。男人脚程略快，进了店，拴门，店内冷清，只得我与老板小二三人。老板和小二早已吓得钻进案桌底下瑟瑟发抖，我坐在那里，手放到碗边的剑囊上。男人也是异族人，扑过来，用番语喊：“酒窖在哪？酒窖在哪？”我侠义心又起，走过去扒开案桌。老板一边尿裤子一边指：“内房有一只铺苍狼皮的红木躺椅……”我搀着男人进去，我知道，他想借酒窖逃过门外追兵。凡边塞有名的酒馆，店家一定会有祖传秘酿做酒种，为防外泄，常在地底做一个迷宫一样的酒窖。窖内四通八达，只有一个出口通往藏秘酿的地穴，那穴为了酿酒时的通风防潮，通常是留出了风口在戈壁里，或者朝向一面丘壁，或者通往一片胡杨林，假如进了酒窖找到出口，这一场劫难，该是能避过了。我与那男人进…

Effective C# 原则27：避免使用ICloneable
type: item
date: 2013-10-07T07:28:00+08:00
description: ICloneable看上去是个不错的主意：为一个类型实现ICloneable接口后就可以支持拷贝了。如果你不想支持拷贝，就不要实现它。但你的对象并不是在一个“真空”的环境中运行，但考虑到对派生类的些影响，最好还是对ICloneable支持。一但某个类型支持ICloneable,那么所有的派生类都必须保持一致，也就是所有的成员必须支持ICloneable接口或者提供一种机制支持拷贝。最后，支持深拷贝的对象，在创建设计时如果包含有网络结构的对象，会使拷贝很成问题。ICloneable也觉察到这个问题，在它的官方定义中有说明：它同时支持深拷贝和浅拷贝。浅拷贝是创建一个新的对象，这个新对象对包含当前对象中所有成员变量的拷贝。如果这些成员变量是引用类型的，那么新的对象与源对象包含了同样的引用。而深拷贝则可以很好的拷贝所有成员变量，引用类型也被递归的进行了拷贝。对于像整型这样的内置类型，深拷贝和浅拷贝是一样的结果。哪一种是我们的类型应该支持的呢？这取决于类型本身。但同时在一个类型中混用深拷贝和浅拷贝会导致很多不一致的问题。一但你涉及到ICloneable这个问题，这样的混用就很难解脱了。大多数时候，我们应该完全避免使用ICloneable，让类更简单一些。这样使用和实现都相对简单得多。任何只以内置类型做为成员的值类型不必支持ICloneable;用简单的赋值语句对结构的所有值进行拷贝比Clone()要高效得多。Clone()方法必须对返回类型进行装箱，这样才能强制转化成一个System.Object的引用。而调用者还得再用强制转化从箱子中取回这个值。我知道你已经有足够的能力这样做，但不要用Clone()函数来取代赋值语句。那么，当一个值类型中包含一个引用类型时又会怎样呢？最常见的一种情况就是值类型中包含一个字符串：public struct ErrorMessage { private int errCode; private int details; private string msg; // details elided } 字符串是一个特殊情况，因为它是一个恒定类。如果你指定了一个错误消息串，那么所有的错误消息类都引用到同一个字符串上。而这并不会导致任何问题，这与其它一般的引用类型是不一样的。如果你在任何一个引用上修改了msg变量，你会就为它重新创建了一个string对象(参见原则7)。(译注：string确实是一个很有意思的类，很多C++程序员对这个类不理解，也很有一些C\#程序对它不理解，导致很多的低效，甚至错误问题。应该好好的理解一下C\#里的string(以及String和StringBulider之间的关系)这个类，这对于学好C\#是很有帮助的。因为这种设计思想可以沿用到我们自己的类型中。)一般情况，如果一个结构中包含了一个任意的引用类型，那么拷贝时的情况就复杂多了。这也是很少见的，内置的赋值语句会对结构进行浅拷贝，这样两个结构中的引用变量就引用到同个一个对象上。如果要进行深拷贝，那么你就必须对引用类型也进行拷贝，而且还要知道该引用类型上是否也支持用Clone()进行深拷贝。不管是哪种情况，你都不用对值类型添加对ICloneable的支持，赋值语句会对值类型创建一个新的拷贝。一句概括值类型：没有任何理由要给一个值类型添加对ICloneable接口的支持!好了，现在让我们再看看引用类型。引用类型应该支持ICloneable接口，以便明确的给出它是支持深拷贝还是浅拷贝。明智的选择是添加对ICloneable的支持，因为这样就明确的要求所有派生类也必须支持ICloneable。看下面这个简单的继承关系：class BaseType : ICloneable { private string _label = "class name"; private int [] _values = new int [ 10 ]; public object Clone() { BaseType rVal = new BaseType( ); rVal._label = _label; for( int i = 0; i < _values.Length; i++ ) rVal._values[ i ] = _values[ i ]; return rVal; } } class Derived : BaseType { private double [] _dValues = new double[ 10 ]; static void Main( string[] args ) { Derived d = new Derived(); Derived d2 = d.Clone() as Derived; if ( d2 == null ) …

JQuery操作html Table的一些好方法
type: item
date: 2013-10-06T06:53:00+08:00
description: 一、数据准备 文章标题 文章分类 发布时间 操作 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 文章标题 文章分类 发布时间 操作 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 文章标题 文章分类 发布时间 操作 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 文章标题 文章分类 发布时间 操作 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试 测试3 测试 测试 测试 二、操作 //1.鼠标移动行变色 $("#table1 tr").hover(function(){ $(this).children("td").addClass("hover") },function(){ $(this).children("td").removeClass("hover") }) $("#table2 tr:gt(0)").hover(function() { $(this).children("td").addClass("hover"); }, function() { $(this).children("td").removeClass("hover"); }); //2.奇偶行不同颜色 $("#table3 tbody tr:odd").css("background-color", "#bbf"); $("#table3 tbody tr:even").css("background-color","#ffc"); $("#table3 tbody tr:odd").addClass("odd") $("#table3 tbody tr:even").addClass("even") //3.隐藏一行 $("#table3 tbody tr:eq(3)").hide(); //4.隐藏一列 $("#table5 tr td::nth-child(3)").hide(); $("#table5 tr").each(function(){$("td:eq(3)",this).hide()}); //5.删除一行 // 删除除第一行外的所有行 $("#table6 tr:not(:first)").remove(); //6.删除一列 // 删除除第一列外的所有列 $("#table6 tr td:not(:nth-child(1))").remove(); //7.得到（设置）某个单元格的值 //设置table7,第2个tr的第一个td的值。 $("#table7 tr:eq(1) td:nth-child(1)").html("value"); //获取table7,第2个tr的第一个td的值。 $("#table7 tr:eq(1) td:nth-child(1)").html(); //8.插入一行： //在第二个tr后插入一行 $("插入3插入插入插入").insertAfter($("#table7 tr:eq(1)")); <"script>

Effective C# 原则26：用IComparable和IComparer实现对象的顺序关系
type: item
date: 2013-10-04T08:35:00+08:00
description: 你的类型应该有一个顺序关系，以便在集合中描述它们如何存储以及排序。.Net框架为你提供了两个接口来描述对象的顺序关系：IComparable和IComparer。IComparable为你的类定义了自然顺序，而实现IComparer接口的类可以描述其它可选的顺序。你可以在实现接口时，定义并实现你自己关系操作符(\<，\>，\<=，\>=)，用于避免在运行时默认比较关系的低效问题。这一原则将讨论如何实现顺序关系，以便.Net框架的核心可以通过你定义的接口对你的类型进行排序。这样用户可以在些操作上得更好的效率。IComparable接口只有一个方法：CompareTo()，这个方法沿用了传统的C函数库里的strcmp函数的实现原则：如果当前对象比目标对象小，它的返回值小于0；如果相等就返回0；如果当前对象比目标对象大，返回值就大于0。IComparable以System.Object做为参数，因此在使用这个函数时，你须要对运行时的对象进行检测。每次进行比较时，你必须重新解释参数的类型：public struct Customer : IComparable { private readonly string _name; public Customer( string name ) { _name = name; } #region IComparable Members public int CompareTo( object right ) { if ( ! ( right is Customer ) ) throw new ArgumentException( "Argument not a customer", "right" ); Customer rightCustomer = ( Customer )right; return _name.CompareTo( rightCustomer._name ); } #endregion } 关于实现比较与IComparable接口的一致性有很多不太喜欢的地方，首先就是你要检测参数的运行时类型。不正确的代码可以用任何类型做为参数来调用CompareTo方法。还有，正确的参数还必须进行装箱与拆箱后才能提供实际的比较。每次比较都要进行这样额外的开销。在对集合进行排序时，在对象上进行的平均比较次数为Nxlog(N)，而每次都会产生三次装箱与拆箱。对于一个有1000个点的数组来说，这将会产生大概20000次的装箱与拆箱操作，平均计算：Nx log(n)有7000次，每次比较有3次装箱与拆箱。因此，你必须自己找个可选的比较方法。你无法改变IComparable.CompareTo()的定义，但这并不意味着你要被迫让你的用户在一个弱类型的实现上也要忍受性能的损失。你可以重载CompareTo()方法，让它只对Customer对象操作：public struct Customer : IComparable { private string _name; public Customer( string name ) { _name = name; } #region IComparable Members // IComparable.CompareTo() // This is not type safe. The runtime type // of the right parameter must be checked. int IComparable.CompareTo( object right ) { if ( ! ( right is Customer ) ) throw new ArgumentException( "Argument not a customer", "right" ); Customer rightCustomer = ( Customer )right; return CompareTo( rightCustomer ); } // type-safe CompareTo. // Right is a customer, or derived from Customer. public int CompareTo( Customer right ) { return _name.CompareTo( right._name ); } #endregion } 现在，IComparable.CompareTo()就是一个隐式的接口实现，它只能通过IComparable接口的引用才能调用。你的用户则只能使用一个类型安全的调用，而且不安全的比较是不可能访问的。下面这样无意的错误就不能通过编译了：Customer c1; Employee e1; if ( …