Be and aware of who you are.
置顶随笔
摘要: 别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并不像你想像中的那么困难。当然,如果你看完了这篇教程之后发现自己明白了很多,却又几乎什么都记不得,那也是很正常的--其实我认为没接触过正则表达式的人在看完这篇教程后能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本道理,以后你还需要多练习,多查资料,才能熟练掌握正则表达式。
阅读全文
2009年4月5日
写过一个负责对象克隆功能的类,最近改进了一下,提升了30%的效率:

Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Testing
{
public static class CloneHelper
{
public static T DeepClone<T>(this T obj)
{
if (obj == null)
return default(T);
var t = obj.GetType();
if (t.IsValueType || t == typeof(string))
return obj;
if (t.IsArray)
return (T)(object)CloneArray((Array)(object)obj, t.GetElementType(), true);
var clone = GetObjectCloner(t, true);
return (T)clone(obj);
}
public static T ShallowClone<T>(this T obj)
{
if (obj == null)
return default(T);
var t = obj.GetType();
if (t.IsValueType || t == typeof(string))
return obj;
if (t.IsArray)
return (T)(object)CloneArray((object[])(object)obj, t.GetElementType(), false);
var clone = GetObjectCloner(t, false);
return (T)clone(obj);
}
private static Array CloneArray(Array array, Type elementType, bool deepclone)
{
var length = array.Length;
var result = Array.CreateInstance(elementType, length);
for (var i = 0; i < length; i++)
{
var element = array.GetValue(i);
var cloned = deepclone
? DeepClone(element)
: element;
result.SetValue(cloned, i);
}
return result;
}
static MethodInfo deepclone_object = StrongTypeReflector.Static.Method(() => CloneHelper.DeepClone<object>(null));
static Dictionary<Type, Func<object, object>> deepcache = new Dictionary<Type, Func<object, object>>();
static Dictionary<Type, Func<object, object>> shallowcache = new Dictionary<Type, Func<object, object>>();
private static Func<object, object> GetObjectCloner(Type type, bool deepclone)
{
Func<object, object> result;
var cache = deepclone
? deepcache
: shallowcache;
if (!cache.TryGetValue(type, out result))
{
var param = Expression.Parameter(typeof(object), "x");
var bindings = new List<MemberAssignment>();
foreach (var field in GetFields(type))
{
var t = field.FieldType;
if (t.IsSubclassOf(typeof(Delegate)))
continue;
var value = Expression.Field(Expression.Convert(param, type), field);
var cloned = (!deepclone) || t.IsValueType || t == typeof(string)
? (Expression)value
: Expression.Convert(Expression.Call(deepclone_object, value), field.FieldType);
bindings.Add(Expression.Bind(field, cloned));
}
var init = Expression.MemberInit(Expression.New(type), bindings.ToArray());
result = Expression.Lambda<Func<object, object>>(init, param).Compile();
cache.Add(type, result);
}
return result;
}
private static IEnumerable<FieldInfo> GetFields(Type type)
{
IEnumerable<FieldInfo> fields = Enumerable.Empty<FieldInfo>();
var t = type;
while (t != null)
{
fields = fields.Concat(t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
t = t.BaseType;
}
return fields;
}
}
}
2009年3月16日

Description
jQuery.combobox is a lightweight jQuery plugin which transforms a textbox into a combobox, so users can enter text or select from supplied options.
It could be called more than once on the same textbox, however only the first time calling counts -- which means you can't change the suggestions once the combobox is created.
It would work with jQuery.bgiframe plugin to fix the IE select/zIndex bug if you include it in the same page.
Tested in IE 7, Firefox 3.
Updates
- 2009-3-18
- Suggestion matching is now case-insensitive
- The second time setting up combobox on the same textbox is working now
- Fixed the IE relative z-index bug by avoiding relative positioning
Demostartion
Usage
Html
<input class="combo" />
Script
<link href="jquery.combobox.css" rel="stylesheet" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.combobox.min.js"></script>
jQuery('.combo').combobox(['option1', 'option2', 'option3', ...], {imageUrl : '/images/dropdown.gif'});
Copyright
It's released under the MIT licence, so use it however you want.
By deerchao@gmail.com on 2009-3-16
Download
http://files.cnblogs.com/deerchao/jquery.combobox.zip
2009年3月7日
今天买了一台内置刻录机,安装完成后发现机箱提供的面板设计不太合理:光驱门打开后,按钮就根本没有用了,因为它附着在门上,与光驱本身提供的开仓按钮根本连不上了,于是我只能用手推动光盘托才能把光驱门给关上.

郁闷之余,写了一个小工具来解决这个问题:

最核心的东西--开关光驱门,其实很简单:

Code
public static void Open(string driveLetter)
{
string returnString = "";
mciSendStringA("set cdaudio!" + driveLetter + " door open", returnString, 0, 0);
}
public static void Close(string driveLetter)
{
string returnString = "";
mciSendStringA("set cdaudio!" + driveLetter + " door closed", returnString, 0, 0);
}
[DllImport("winmm.dll", EntryPoint = "mciSendStringA")]
public static extern void mciSendStringA(string lpstrCommand, string lpstrReturnString, long uReturnLength, long hwndCallback);
上面的代码是从网上搜索得到的.我本来想判断一下光驱本身的状态--是开还是关,但是搜索了十来分钟,没找到相关资料,觉得这个也不是特别有必要,于是放弃了.
好了,下面就要获取系统里所有光驱了,其实很简单,一行代码就搞定:

Code
public static string[] GetCDRoms()
{
return DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.CDRom).Select(x => x.Name).ToArray();
}
为了让程序用起来更方便,我希望它能设置为随Windows自动启动.这个也没什么困难的,就是操作注册表而已,于是我从原来写的代码里扒出了一段,重新加工了一下:

Code
class AutoLaunch
{
string registryKey;
public AutoLaunch(string registryKey)
{
this.registryKey = registryKey;
}
string GetRunningExePath()
{
return Application.ExecutablePath;
}
public bool Enabled
{
get
{
try
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software");
key = key.OpenSubKey("Microsoft");
key = key.OpenSubKey("Windows");
key = key.OpenSubKey("CurrentVersion");
key = key.OpenSubKey("Run", true);
string cmdline = key.GetValue(registryKey) as string;
if (cmdline == null)
{
return false;
}
else
{
if (!string.Equals(cmdline, GetRunningExePath(), StringComparison.OrdinalIgnoreCase))
key.SetValue(registryKey, GetRunningExePath());
return true;
}
}
catch (NullReferenceException)
{
return false;
}
}
set
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software");
key = key.OpenSubKey("Microsoft");
key = key.OpenSubKey("Windows");
key = key.OpenSubKey("CurrentVersion");
key = key.OpenSubKey("Run", true);
if (value)
{
key.SetValue(registryKey, GetRunningExePath());
}
else
{
key.DeleteValue(registryKey, false);
}
}
}
}
好了,现在可以做界面了.由于程序非常的简单,我选择了使用Windows Forms,而且决定一个窗口都不要,用户能看到的全部界面就是通知区域的一个图标.
那好的,在一个空白的Form上扔一个NotifyIcon,和一个ContextMenuStrip,并把NotifyIcon的ContextMenuStrip属性设置好.
然后就是我们的代码了,用于在Form的构造时,动态生成菜单内容:

Code
AutoLaunch autoLaunch;
public Form1()
{
InitializeComponent();
Init();
}
private void Init()
{
ShowInTaskbar = false;
WindowState = FormWindowState.Minimized;
autoLaunch = new AutoLaunch("CDTool_deerchao");
LoadMenu();
}

Code
private void LoadMenu()
{
var menuItems = new List<ToolStripItem>();
menuItems.Add(new ToolStripMenuItem
{
Text = "By deerchao",
});
menuItems.Add(new ToolStripSeparator());
foreach (var cd in CDDrive.GetCDRoms())
{
menuItems.AddRange(LoadMenu(cd).Cast<ToolStripItem>());
menuItems.Add(new ToolStripSeparator());
}
var autoStart = new ToolStripMenuItem("&Start with Windows", null, (s, e) => autoLaunch.Enabled = ((ToolStripMenuItem)s).Checked)
{
CheckOnClick = true,
Checked = autoLaunch.Enabled,
};
var exit = new ToolStripMenuItem("E&xit", null, (s, e) => Application.Exit());
menuItems.Add(autoStart);
menuItems.Add(exit);
contextMenu.Items.AddRange(menuItems.ToArray());
}
private IEnumerable<ToolStripMenuItem> LoadMenu(string cd)
{
var open = new ToolStripMenuItem("&Open " + cd.Substring(0, 2), null, (s, e) => CDDrive.Open(cd));
var close = new ToolStripMenuItem("&Close " + cd.Substring(0, 2), null, (s, e) => CDDrive.Close(cd));
yield return open;
yield return close;
}
很好,现在运行起来就一切OK了.
但是,追求完美的我们怎么会如此轻易罢休呢..我们可以看到,代码里多处用到了Linq扩展方法,比如Where, Cast, Select, ToArray等. 而Linq则是.Net 3.5里才有的新东西..那意味着这个程序的用户,要去下载200多M的.Net 3.5安装程序,然后花两个小时来安装它,最后才能使用这个不到200K的小工具...
怎么办?打造山塞版的Linq吧,还好,我们使用到的Linq特性并不多,只要几十行代码就能完成:

Code
using System.Collections;
using System.Collections.Generic;
namespace System.Linq
{
delegate TResult Func<TSource, TResult>(TSource source);
static class Enumerable
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Predicate<T> predict)
{
foreach (var v in source)
if (predict(v))
yield return v;
}
public static IEnumerable<TDest> Cast<TDest>(this IEnumerable source)
{
foreach (var v in source)
yield return (TDest)v;
}
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> mapping)
{
foreach (var v in source)
yield return mapping(v);
}
public static List<T> ToList<T>(this IEnumerable<T> source)
{
return new List<T>(source);
}
public static T[] ToArray<T>(this IEnumerable<T> source)
{
return ToList(source).ToArray();
}
}
}
namespace System.Runtime.CompilerServices
{
class ExtensionAttribute : Attribute
{
}
}
OK,把针对的.Net Framework版本改成2.0,砍掉不必要的引用项,重新编译,搞定!
现在唯一让我不爽的就是没有一个好图标了...好吧,我们从微软那里借一个来吧.操起刚Google来的图标编辑工具IcoFx,在File\Extract里选择C:\Windows\System32\imagers.dll,从里边挑选一个光盘图标,保存.
在项目里设置好图标以后,重新编译,That's All!
你可以从这里下载全部源代码.
2009年2月23日

Code
// Enable user to move focus in a grid of inputs with Ctrl + Up, Down, Left, Right, Home, End
// Could be called again after more rows are created.
// deerchao@gmail.com 2009-2-23
// usage:
//--------------Html----------------
// <tr class="dataRow">
// <td><input /></td>
// <td><input /></td>
// <td><input /></td>
// </tr>
// <tr class="dataRow">
// <td><input /></td>
// <td><input /></td>
// <td><input /></td>
// </tr>
//--------------Script-------------
// jQuery.excel('dataRow');
jQuery.extend({
excel: function(rowClass) {
var keys = { left: 37, up: 38, right: 39, down: 40, home: 36, end: 35 };
rowClass = rowClass ? rowClass : '.excel';
if (rowClass[0] != '.')
rowClass = '.' + rowClass;
$(rowClass).unbind('keyup',onkeyup).bind('keyup',onkeyup);
function onkeyup(evt) {
var ctrlOnly = evt.ctrlKey && !evt.altKey && !evt.shiftKey;
switch (evt.keyCode) {
case keys.down:
go("down");
break;
case keys.up:
go("up");
break;
case keys.left:
if (ctrlOnly)
go("left");
break;
case keys.right:
if (ctrlOnly)
go("right");
break;
case keys.home:
if (ctrlOnly)
go("home");
break;
case keys.end:
if (ctrlOnly)
go("end");
break;
}
function go(to) {
var td = $(evt.target).closest('td');
var tr = $(evt.currentTarget);
var toFocus = null;
switch (to) {
case 'home':
toFocus = lastInput(td.prevAll('td'));
break;
case 'end':
toFocus = lastInput(td.nextAll('td'));
break;
case 'left':
toFocus = firstInput(td.prevAll('td'));
if (!toFocus)
toFocus = lastInput(tr.prev('tr' + rowClass).children('td'));
break;
case 'right':
toFocus = firstInput(td.nextAll('td'));
if (!toFocus)
toFocus = firstInput(tr.next('tr' + rowClass).children('td'));
break;
case 'up':
toFocus = firstInput(tr.prev('tr' + rowClass).children('td'), td.prevAll('td').size());
break;
case 'down':
toFocus = firstInput(tr.next('tr' + rowClass).children('td'), td.prevAll('td').size());
break;
}
if (toFocus) {
toFocus.focus();
}
}
function firstInput(tds, start) {
if (!start)
start = 0;
for (var i = start; i < tds.size(); i++) {
var inputs = $(tds[i]).children('input, select, textarea');
if (inputs.size())
return inputs[0];
}
return null;
}
function lastInput(tds) {
for (var i = tds.size() - 1; i >= 0; i--) {
var inputs = $(tds[i]).children('input, select, textarea');
if (inputs.size())
return inputs[0];
}
return null;
}
}
}
});
2009年1月22日
一直没去细想为什么Enumerable和Queryable提供了两套签名一致的方法.今天在扩展IQToolkit,使之支持Insert/Update/Delete的过程中,遇到了问题,用Reflector看了一下Queryable类的源码,顿时醍醐灌顶.
在给代表数据库表格的类EntityTable(实现了IQueryable<TEntity>接口)添加一个扩展方法Delete(Expression<Predicate<TEntity>>)来做生成Delete语句的标识时,发现这个方法被直接调用了,觉得很郁闷.明明微软自己的Where, Select等方法都没被调用,而是作为Expression给传到了QueryProvider里, 凭什么我自己写的方法你就不给翻译一下传过去,而是当场执行呢?
不对啊,First, Single之类的方法也是当场执行的啊,难道编译器会根据方法的返回类型(是不是相同的IQueryable<T>)来决定如何对待它们?或者,可能有某个应用于方法上的Attribute,可以控制编译器这方面的行为?
还是看看Queryable里的代码是怎么写的吧.打开Reflector一看Queryable.First方法,马上明白了:
public static TSource First<TSource>(this IQueryable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return source.Provider.Execute<TSource>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression }));
}
晕了,原来根本不是编译器在捣鬼!这方法纯粹是一混蛋,屁事不干的主.不单屁事不干,还指使别人说,你再来调用我吧..
再看Queryable.Where:
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
if (predicate == null)
{
throw Error.ArgumentNull("predicate");
}
return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Quote(predicate) }));
}
First和Where的区别在于两者调用了不同的方法,一个要求直接执行对应的动作,一个要求返回一个新的IQueryable<T>对象以推迟实际行为的发生.
哎,这世界上怎么那么多比我聪明的人呢?