相信使用过Java或者是.NET的人对动态数组类Array(或者是类似的类)都不会陌生——因为这个类好处多多,可以无限扩展其长度大小范围,以便容纳更多的内容。其实在java中也有一个Array对象可以使用,它几乎也是万能的:
【一】当动态数组使用:
这是最最基本的Array使用方法,Array对象有三个“构造函数”:
new Array();
new Array(size);
new Array(element0, element1, ..., elementn);
第一个指定一个空的Array,第二个指定一个长度的Array,最后一个直接填充数据初始化长度和若干内容的Array。无论哪一种,一旦不够用了都可以往里边直接添加数据。添加数据的方法是:
ArrayInstance[索引号,从0开始]=内容;
当然您也可以通过for的方式循环遍历(注意:javascript根本没有int这种显式声明的类型,一律是弱类型的var)
for(var i=起始索引;i<arrayInstance.length;++i) { alert(arrayInstance[i]); }
另外你还可以使用foreach方式(注意,javascript中没有foreach,还是用for的方式,有些类似java的for(变量in集合))
var arr = new Array(1, 2, 3); for (var i in arr) { alert(i); }
以上做法输出会有惊人的发现——竟然是0,1,2而不是1,2,3!Why?注意这里和C#或者java最大的不同在于——C#或者java是把foreach理解成IEnumerator接口对象,访问封装的内部数据;但是js中这样做导致的结果是你只能访问索引号而不是索引的内容,因此你正确的做法是:
var arr = new Array(1, 2, 3); for (var i in arr) { alert(arr[i]); }
除此之外,您还可以这样初始化数组:
var arr=[1,2,3,……,内容];
【当字典使用】
下标不一定非要是数字(默认0开始),Array允许你直接自定义下标,这样一来使用过Dictionary的人往往乐开了花:
var arr = new Array(); arr["a"] = "a"; arr["b"] = "b";
但是注意,此时使用arr.length检测的结果是0!Why?因为这些并不是js的真正下标而是属性而已,属性不算在arr的长度之列,只有0,1,2……这样的下标才算。
遍历这些属性方法同遍历索引一样(使用for版本的foreach)。
之前说过当字典使用的时候属性并非下标,那么属性和下标区别是什么呢?一般地,js语法非常松散,属性并不需要“定义”后才“使用”,允许对已有的“方法类”(对象,并不是真正意义上的类)边动态添加边操作——
function MyFun() { this.A = "a"; } var mf = new MyFun(); mf.B = "B"; mf["C"] = "C"; alert(mf.B); alert(mf.C); alert(mf.A);
注意看粗体部分,就明白了,其实这是一个js的语法范畴的问题,Array作为一个对象也可以这样操作,任意一个js对象都可以这样操作,但是请记住,该对象必须通过new出来这样操作方可,直接调用(比如MyFun.B)是不可以的!
另外,这种“字典型”操作方式还可以和json媲美,因此字典型的初始化还允许:
var a = { "A": "A","B":"B" };
var 变量名 = {"键1":"值1","键2":"值2",……,"键n":"值n"};
访问如同for版的foreach,而且允许动态添加键值对。
window是javascript的一个非常重要的核心对象。对于可以打开多个页面的浏览器(比如IE7等以上版本,或是FireFox,Opera……)window只针对当前的客户端窗体有效。下面介绍以下window的一些重要属性以及应用:
【屏蔽右键】
某些网站不允许复制,怎么办呢?办法就是屏蔽鼠标右键。代码可以这样写(注意:event.button属性必须且只能和mouse相关的事件——比如onmouseup/onmousedown/onmouseover使用)。
<body onmousedown="disableRightClick()"> <p> 点击此区域将无法弹出右键 </p> </body>
脚本:
function disableRightClick() { //右键在IE为2 if (event.button == 2) { alert("禁止使用右键!");
return false;
}
}
以上方法在IE中有效,Chrome无效(会弹出警告框和右键菜单)。要禁用几乎全部浏览器的鼠标右键,可以使用document.oncontentmenu属性:
<head> <title></title> <script src="MyJavaScript.js" type="text/javascript"></script> <script> document.oncontextmenu = function () { alert("禁止右键!"); return false;}; </script> </head> <body> </body>
注意:由于oncontentmenu是属性,所以在绑定一个匿名事件的时候必须有返回值(return false)不能省略!
这里顺便说一声:任何Html标签的onXXX事件可以绑定一个匿名事件,同样也可以绑定一个已定义的js函数。该函数根据实际情况可以选择有或者没有返回值。
【Html文本框只能输入数字】
对于限制某些内容在文本框内的输入,可以使用window.event.keyCode配合(限于onkeypress/onkeydown/onkeyup)中
例如以下文本框只允许输入数字:
<script type="text/javascript"> function AutoCheck() { var code = window.event.keyCode if (!(code >= 48 && code <= 57)) { return false; } } </script>
对应Html:
<input type="text" id="myText" onkeypress="return AutoCheck(event);" />
注意input标签中的return不能省略,因为该函数有返回值(如果省略会导致该事件继续执行,因为事件没有被取消)。
不过当把js语句中的return false改成window.event.returnValue=false;的时候是可以省略input标签中的return的,究其原因是因为event.returnValue赋布尔类型的值会决定该事件是否被执行(具体来说,我个人认为应该是阻止事件结果的产生,并不是取消事件!)。
进一步证明,现在我写这样的代码:
<script type="text/javascript">
function AutoCheck(event) {
event.returnValue = false;
alert("OK");
}
</script>
对应Html中:
<input type="text" id="myText" onkeypress="AutoCheck(event);" />
注意:当按下任意键的时候,该事件仍然被触发(弹出OK对话框,说明该事件并没有被取消,而是事件导致的结果——本来应该在文本框中输入的内容字符不能被输入了!)
而如果使用return false取代event.returnValue的话则不仅框不能弹出,而且什么也输入不了(因为return false也会取消事件的结果,并且因为return直接向事件属性返回一个布尔值,最终后继事件代码无法执行;相对而言,event.returnValue只是取消当前事件执行的结果的发生,并不影响后继代码的执行)。
某个客户询问我关于SelectSingleNode和SelectNodes的XPath操作,问题如下:
【xml】
<root>
<dsobject classname="Rendition">
<contentelements>
<contentelement>Tansion</contentelement>
</contentelements>
</dsobject>
<dsobject classname="Rendition">
<contentelements>
<contentelement>Herry</contentelement>
</contentelements>
</dsobject>
<dsobject classname="Rendition">
<contentelements>
<contentelement>Jack</contentelement>
</contentelements>
</dsobject>
</root>
现在他要求“筛选出所有名称为dsobject,classname等于Rendition”,输出其contentelement的数据。
他这样做:
[C#]
foreach (XmlNode item in document.SelectNodes("//dsobject[@classname='Rendition']"))
{
XmlNode oneNode = item.SelectSingleNode("//contentelement");
string inner = oneNode.InnerText;
}
[VB.NET]
For Each item As XmlNode In document.SelectNodes("//dsobject[@classname='Rendition']")
Dim oneNode As XmlNode = item.SelectSingleNode("//contentelement")
Dim inner As String = oneNode.InnerText
Next
粗看先是从xml文档全部选出classname属性值为Rendition的全部dsobject(外循环),内部再从每个dsobject节点内部筛选出contentelement节点并输出包含的数值。但是实际上输出的结果只是三个“Tansion”。
究其原因,是因为XPath语法的特殊性:凡是以“//”开头的,都是无条件以整个xml的根节点开始遍历的。因此item.SelectSingleNode不要误以为是接着上面的dsobject节点往下检索,而是从头开始的!因此遍历的时候总是遍历到第一个dsobject中的contentelement的内部数值。
同样地,凡是以“/”开头的,都是无条件人为指定从根节点开始的绝对路径搜索,与XmlNode上下文毫无关系(比如要查询第一个contentelement,用绝对路径甚至可以这样写):
[C#]
Console.WriteLine(document.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText);
Console.WriteLine(document.DocumentElement.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText);
[VB.NET]
Console.WriteLine(document.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText)
Console.WriteLine(document.DocumentElement.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText)
显然,以/开头无论XmlNode是什么,总是被忽略且从指定的root=>dsobject=>contentelements=>contentment这样的顺序开始检索(绝对路径)。
至于和上下文相关的,就是直接写名字;比如:
[C#]
Console.WriteLine(document.SelectSingleNode("root/dsobject/contentelements/contentelement").InnerText);
Console.WriteLine(document.DocumentElement.SelectSingleNode("dsobject/contentelements/contentelement").InnerText);
[VB.NET]
Console.WriteLine(document.SelectSingleNode("root/dsobject/contentelements/contentelement").InnerText)
Console.WriteLine(document.DocumentElement.SelectSingleNode("dsobject/contentelements/contentelement").InnerText
显然,document的根节点是“#document”(特殊节点,xml中不存在),那么相对它而言下面的节点是root/dsobject/contentelments/contentelement。不过document.DocumentElement本身就是root,因此第二句代码中root就不能再写了,不然会发生找不到节点(因为不存在root=>root=>……这样的节点)。
SelectNodes也符合这样的特点,这里就不再叙述了。
我们知道,在C#中匿名方法支持多行(有、无)返回值处理程序,不过在VisualBasic中似乎并不这样支持多行的函数或者是过程。怎么办呢?我们来看一个例子(拿List(Of T)做例子)。
我们知道List(Of T)其中带有Find和FindAll函数——该两个函数需要一个函数Predicate(Of T) As Boolean作为例子,那么我们直接可以尝试这样做(示例性代码):
Public Function MyFunc(t As T) As Boolean ……………… End Function List(Of T).Find/FindAll(MyFunc) ………………
其实我们可以封一下:
Public MustInherit Class BasePredicate(Of T As IComparable)
Public Shared Widening Operator CType(ByVal subclass As BasePredicate(Of T)) As Predicate(Of T)
Return New Predicate(Of T)(AddressOf subclass.GeneralDo)
End Operator
Public MustOverride Function GeneralDo(ByVal t As T) As Boolean
End Class
该类使用了CType的Widening方法(自动把类转化成一个Predicate,而Predicate的需要参数恰巧是子类必须实现的抽象类GeneralDo,比如子类这样实现:
Public Class IntFindAll
Inherits BasePredicate(Of Integer)
Public Overrides Function GeneralDo(t As Integer) As Boolean
Return t Mod 2 = 0
End Function
End Class
那么子类可以这样调用:
Public Class Program
Shared Sub Main(args As String())
Dim numbers As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
For Each i As Integer In numbers.FindAll(New IntFindAll())
Console.WriteLine(i)
Next
End Sub
End Class
我想,这个可以弥补一些因为在VS2008中VB缺失了C#应有的功能而导致VB某些程序员“心里不平衡”吧,啊哈哈……。
.NET4中加入了并行机制——所谓并行就是同时开辟若干个线程来进行计算。这些线程由于都是互相独立的,所以在进行一些分布式(比如各自不同的工作)的时候是非常简单,不过要把这些处理结果汇总起来却不是那么容易——下面来看一个非常简单的例子(求1~1000的和)。
如果你尝试使用以下的代码计算,恐怕令你大跌眼镜!
[C#]
int sum = 0; Parallel.For(0, 1000,i => {sum+=i;});
[VB.NET]
Dim sum As Integer = 0 Parallel.[For](0, 1000, Function(i) sum += i)
究其原因就是.NET会默认开辟一些线程同时进行“sum+=i”的计算。那么由于sum被这些线程同时使用,往往是一个线程还没有处理完毕,另外一个线程又介入了,自然无法得到正确结果了。
解决这个问题的办法有许多:
【一】分解法:
所谓分解法,就是针对“同一个变量”被不同线程“共享”这一诟病而提出的。——也就是说,把1~1000求和分成若干块进行处理(等于给每一个线程分配了不同的内存)。最后把分布计算的结果进行累计汇总即可。结果如下:
[C#]
int[] numbers = Enumerable.Range(1, 1000).ToArray(); int[] values=new int[4]; int sum = 0; Parallel.For(0, 4, i => { values[i] = new Program().GetTotal(i * 250, 250, numbers); }); sum = values.Sum(); Console.WriteLine(sum);
[VB.NET]
Dim numbers As Integer() = Enumerable.Range(1, 1000).ToArray() Dim values As Integer() = New Integer(3) {} Dim sum As Integer = 0 Parallel.[For](0, 4, Function(i) values(i) = New Program().GetTotal(i * 250, 250, numbers)) sum = values.Sum() Console.WriteLine(sum)
【二】使用静态变量
静态变量本身就具备“同步”的能力(这种例子在简单工厂中也可以窥见)。所以这里显然可以使用,代码如下:
[C#]
public class Program { static int sum = 0; static void Main(string[] args) { Parallel.For(1, 1001, i => {sum+=i;}); Console.WriteLine(sum); } }
[VB.NET]
Public Class Program Shared sum As Integer = 0 Private Shared Sub Main(args As String()) Parallel.[For](1, 1001, Sub(i) sum += i) Console.WriteLine(sum) End Sub End Class
【三】使用volatile变量(一般编译器为了优化性能,往往把常用的变量复制一份到寄存器中,然后直接对寄存器进行操作,等到操作完毕之后再写回原来的内存中。多线程会导致寄存器中的变量不同步,所以结果也不对。而volatile就是告诉编译器直接从内存中读取那个数字进行操作,而不是拷贝到寄存器之后处理):
[C#]
public class Program { volatile int sum = 0; public void ShowResult() { Parallel.For(1, 1001, i => { sum+=i; }); Console.WriteLine(sum); } static void Main(string[] args) { Program p = new Program(); p.ShowResult(); } }
[VB.NET,由于VB.NET没有此特性,所以使用Thread.VolitaleRead每次读取最新的数值后进行累加]
Public Class Program Dim sum As Integer = 0 Public Sub ShowResult() Parallel.For(1, 1001, Sub(i) Thread.VolatileRead(sum) '总是读取最新数值 sum = sum + i Thread.Sleep(2) End Sub) Console.WriteLine(sum) End Sub Public Shared Sub Main(args As String()) Dim p As New Program() p.ShowResult() End Sub End Class
【四】使用lock(锁住一个变量,然后直到该线程操作完毕自动释放变量,另外一个线程进来操作……如此反复而已):
[C#]
public class Program { int sum = 0; public void ShowResult() { object obj = new object(); Parallel.For(1, 1001, i => { lock (obj) { sum += i; Thread.Sleep(10); } }); Console.WriteLine(sum); } static void Main(string[] args) { Program p = new Program(); p.ShowResult(); } }
[VB.NET]
Public Class Program Private sum As Integer = 0 Private obj As New Object Public Sub ShowResult() Dim obj As New Object() Parallel.For(1, 1001, Sub(i) SyncLock obj sum = sum + i End SyncLock End Sub) Console.WriteLine(sum) End Sub End Class Module M Sub Main() Dim p As New Program p.ShowResult() End Sub End Module
【五】使用InternLock函数:
[C#]
public class Program { int sum = 0; public void ShowResult() { Parallel.For(1, 1001, i => { Interlocked.Add(ref sum, i); }); Console.WriteLine(sum); } static void Main(string[] args) { Program p = new Program(); p.ShowResult(); } }
[VB.NET]
Public Class Program Private sum As Integer = 0 Public Sub ShowResult() Parallel.[For](1, 1001, Sub(i) Interlocked.Add(sum, i) End Sub) Console.WriteLine(sum) End Sub Shared Sub Main(args As String()) Dim p As New Program() p.ShowResult() End Sub End Class
上次写了点关于验证码制作的代码,算是学习了Drawing中的一些东西,心里很快活,也乐意为大伙儿做些善事,于是便萌发了把验证码封装成简单的控件发布出去的念头——起初是用ashx(一般处理程序,俗称Httphander)的做法,OK。在WebForm和MVC两种情况下毫无问题——代码很简单,假设把ashx放在程序根目录,那么你就在页面中对应弄上一个img即可:
<img src="/Image.req"/>
这样生成img标签自动也会请求这个handler,生成图片(不要忘记配置handler就可以了)。
然后我想到要做一个dll,因为总觉得把ashx发布出去不是很正规(别人都是发布dll的嘛,呵呵),于是我自己创建了一个类库(ClassLibrary),创建对应的类,实现了IHttpHandler和IRequiresSessionState(拷贝原先ashx中正确的代码,也就是上一篇的代码全部)。然后编译成dll组件,在web.config的<httpHandlers>进行恰当的配置:
<configuration> <system.web> <!---Httphandler's configuration here……--> <httpHandlers> <add path="*.req" verb="*" type="TestLibrary.DrawImage"/> </httpHandlers> ………………
然后在WebForm和MVC中同时实验——结果很奇怪:在WebForm一切照旧正常,可是MVC程序中死活就是红叉叉报错(显示不出图片,郁闷中……)
后来多方请教,直到遇到了大神imran_ku07之后我才恍然大悟(看到 http://forums.asp.net/t/1800318.aspx这篇类似的问题)——原来当你写"/Image.req"的时候,默认路由就会按照“Controller/Action/DefaultOptionalValue”去匹配:这样的话,Controller就变成了“Image.req”,自然没有这个Controller,然后自然报错(图片无法出来)。
解决办法就是让这个路径不要通过默认的Route方式解析,使用IgnoreRoute方式排除这个路径(粗体):
[C#]
public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("{resource}.req/{*pathInfo}"); routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller = "Default", action = "Index", id = UrlParameter.Optional } // 参数默认值 ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); } }
[VB.NET]
Public Class MvcApplication Inherits System.Web.HttpApplication Public Shared Sub RegisterRoutes(routes As RouteCollection) routes.IgnoreRoute("{resource}.axd/{*pathInfo}") routes.IgnoreRoute("{resource}.req/{*pathInfo}") ' 路由名称 ' 带有参数的 URL ' 参数默认值 routes.MapRoute("Default", "{controller}/{action}/{id}", New With { _ Key .controller = "Default", _ Key .action = "Index", _ Key .id = UrlParameter.[Optional] _ }) End Sub Protected Sub Application_Start() AreaRegistration.RegisterAllAreas() RegisterRoutes(RouteTable.Routes) End Sub End Class
这样一来,果然在ASP mvc中也可以咯!神啦!
“福无双至,祸不单行”——下午某时有个中软的程序员找我询问关于如何屏蔽MVC插件的事情(它的项目根目录有plugin文件夹,然后里边有大量其它重要文件,现在不想要别人访问)。我想了以下,给了两个解决方案:
1)在根目录web.config中配置如下(相对于根目录的子目录——plugin中后面子文件夹和文件均无法访问)
<system.web> <httpHandlers> <add path="/plugin/*" verb="*" type="System.Web.HttpNotFoundHandler"/> </httpHandlers> ………………
2)拷贝一个web.config文件到plugin文件夹中,然后这样配置(表示当前文件夹中所有子文件夹和文件均无法访问)
<system.web> <httpHandlers> <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/> </httpHandlers> ………………
他的问题是解决了,我又冒出新问题了——既然MVC可以通过:“http://localhost/文件夹名/文件”的形式直接访问文件,那么它为什么不会把“文件夹名”解析成Controller,“文件”解析成Action呢?我猜想如下:
1)任何网页程序默认“潜规则”解析是根据地址栏输入的虚拟路径寻找对应的文件(传统ASP.NET Web也不例外),因此MVC也有此潜规则——如果地址栏输入的虚拟路径恰好可以映射找到真实的文件,那么直接返回结果,就不会在Route了。
2)如果找不到:
2.1)尝试Route(按照默认或者其它定义规则:http://localhost/{Controller}/{Action}/DefaultOptionalParameter)进行解析。
2.2)如果解析成功,那么返回Action中对应的View,解析失败,抛出异常。
3)如果某个请求地址(比如Image.req纯粹是一个ashx请求,真实文件路径均不存在),直接使用Route规则匹配肯定错误,此时你就应该告知系统“这是例外”(用IgnoreRoute即可)。
附加说明:http://visualstudiogallery.msdn.microsoft.com/24ef1a72-6b1c-428a-9908-8990d5d347f2
我做了一个可以供ASP.NET和MVC使用的验证码控件dll(其中ASP.NET一般页面可以直接使用,MVC的则必须通过一些手动配置才可以使用)。有兴趣可以下载看看哦!
网站上验证码效果一般制作方法是:
1)使用HttpHandler(一般处理程序)绘制随机验证码的图,以及产生随机码,并输出到页面的OutputStream中。
2)页面中使用异步方式(js等)进行刷新当前页面的验证码。
【示例】
1)创建一个“一般应用处理程序ashx”,代码如下:
[C#]
public class ValidationCode : IHttpHandler { //随机发生器 static Random r = new Random(Guid.NewGuid().GetHashCode()); //排除黑色、透明色颜色,因为底色黑色 static PropertyInfo[] colors = (typeof(Brushes).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Static)).Where(p => p.Name != "Black" && p.Name != "Transparent").Select(p => p).ToArray(); //排除黑色颜色,因为底色黑色 static PropertyInfo[] linecolors = (typeof(Pens).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Static)).Where(p => p.Name != "Black").Select(p => p).ToArray(); //获取静态类Brushes实例对象 static object colorobj = typeof(Brushes).GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null); //获取静态类Pens实例对象 static object penobj = typeof(Pens).GetConstructor(BindingFlags.NonPublic, null, Type.EmptyTypes, null); //每个随机字符的宽度 const float PERNUMBERWIDTH = 40.0f; //每个字符的高度 const float PERNUMBERHEIGHT = 50.0f; public void ProcessRequest(HttpContext context) { //获取要产生多少随机数(默认产生5个) int reqNum = 5; if (context.Request.QueryString["reqNum"] != null) { int.TryParse(context.Request.QueryString["reqNum"], out reqNum); } //产生多少大的背景图 Bitmap bt = new Bitmap((int)(PERNUMBERWIDTH*reqNum), (int)PERNUMBERHEIGHT); Graphics g = Graphics.FromImage(bt); //产生4个随机数(number可以被保存到Session中) string numbers = ""; //绘制数字 for (int i = 1; i <= reqNum; i++) { numbers += r.Next(0, 9).ToString(); var color = (PropertyInfo)colors.GetValue(r.Next(0, colors.Length)); context.Response.Write(color.Name + "<br/>"); Brush randomcolor = (Brush)color.GetValue(colorobj, null); g.DrawString(numbers[i-1].ToString(), new Font("黑体", PERNUMBERWIDTH),randomcolor, new PointF((i-1)*PERNUMBERWIDTH, 0f)); } //绘制随机线条 int linenum = r.Next(10, 21); for (int i = 1; i <= linenum; i++) { var linecolor = (PropertyInfo)linecolors.GetValue(r.Next(0, colors.Length)); Pen randomcolor = (Pen)linecolor.GetValue(penobj, null); g.DrawLine(randomcolor, new PointF((float)(r.NextDouble() * PERNUMBERWIDTH * reqNum), (float)(r.NextDouble() * PERNUMBERHEIGHT)), new PointF((float)(r.NextDouble() * PERNUMBERWIDTH * reqNum), (float)(r.NextDouble() * PERNUMBERHEIGHT))); } g.Dispose(); context.Response.Clear(); context.Response.ContentType = "image/jpeg"; bt.Save(context.Response.OutputStream, ImageFormat.Jpeg); bt.Dispose(); context.Response.End(); } public bool IsReusable { get { return false; } } }
[VB.NET]
Public Class ValidationCode Implements IHttpHandler '随机发生器 Shared r As New Random(Guid.NewGuid().GetHashCode()) '排除黑色、透明色颜色,因为底色黑色 Shared colors As PropertyInfo() = (GetType(Brushes).GetProperties(System.Reflection.BindingFlags.[Public] Or System.Reflection.BindingFlags.GetProperty Or System.Reflection.BindingFlags.[Static])).Where(Function(p) p.Name <> "Black" AndAlso p.Name <> "Transparent").[Select](Function(p) p).ToArray() '排除黑色颜色,因为底色黑色 Shared linecolors As PropertyInfo() = (GetType(Pens).GetProperties(System.Reflection.BindingFlags.[Public] Or System.Reflection.BindingFlags.GetProperty Or System.Reflection.BindingFlags.[Static])).Where(Function(p) p.Name <> "Black").[Select](Function(p) p).ToArray() '获取静态类Brushes实例对象 Shared colorobj As Object = GetType(Brushes).GetConstructor(BindingFlags.NonPublic, Nothing, Type.EmptyTypes, Nothing) '获取静态类Pens实例对象 Shared penobj As Object = GetType(Pens).GetConstructor(BindingFlags.NonPublic, Nothing, Type.EmptyTypes, Nothing) '每个随机字符的宽度 Const PERNUMBERWIDTH As Single = 40F '每个字符的高度 Const PERNUMBERHEIGHT As Single = 50F Public Sub ProcessRequest(context As HttpContext) '获取要产生多少随机数(默认产生5个) Dim reqNum As Integer = 5 If context.Request.QueryString("reqNum") IsNot Nothing Then Integer.TryParse(context.Request.QueryString("reqNum"), reqNum) End If '产生多少大的背景图 Dim bt As New Bitmap(CInt(Math.Truncate(PERNUMBERWIDTH * reqNum)), CInt(Math.Truncate(PERNUMBERHEIGHT))) Dim g As Graphics = Graphics.FromImage(bt) '产生4个随机数(number可以被保存到Session中) Dim numbers As String = "" '绘制数字 For i As Integer = 1 To reqNum numbers += r.[Next](0, 9).ToString() Dim color = DirectCast(colors.GetValue(r.[Next](0, colors.Length)), PropertyInfo) context.Response.Write(Convert.ToString(color.Name) & "<br/>") Dim randomcolor As Brush = DirectCast(color.GetValue(colorobj, Nothing), Brush) g.DrawString(numbers(i - 1).ToString(), New Font("黑体", PERNUMBERWIDTH), randomcolor, New PointF((i - 1) * PERNUMBERWIDTH, 0F)) Next '绘制随机线条 Dim linenum As Integer = r.[Next](10, 21) For i As Integer = 1 To linenum Dim linecolor = DirectCast(linecolors.GetValue(r.[Next](0, colors.Length)), PropertyInfo) Dim randomcolor As Pen = DirectCast(linecolor.GetValue(penobj, Nothing), Pen) g.DrawLine(randomcolor, New PointF(CSng(r.NextDouble() * PERNUMBERWIDTH * reqNum), CSng(r.NextDouble() * PERNUMBERHEIGHT)), New PointF(CSng(r.NextDouble() * PERNUMBERWIDTH * reqNum), CSng(r.NextDouble() * PERNUMBERHEIGHT))) Next g.Dispose() context.Response.Clear() context.Response.ContentType = "image/jpeg" bt.Save(context.Response.OutputStream, ImageFormat.Jpeg) bt.Dispose() context.Response.[End]() End Sub Public ReadOnly Property IsReusable() As Boolean Get Return False End Get End Property End Class
注意:
1)一些诸如Brushes等特定因为是公用的,需要通过反射获取全部的颜色属性列表,因此使用了静态变量,这样不必每次都初始化,节省内存和时间。
2)Brushes避免黑色和透明色(本示例背景色是黑色),Pens只需避免黑色即可。有关Brushes颜色,可以查阅:http://msdn.microsoft.com/zh-cn/library/system.windows.media.brush(v=vs.95).aspx
3)Bitmap类是用于绘制使用的,一般是空白的黑色背景。一般配合Image类+Graphics画布使用,进行绘制。
4)BitMap的Save方法有若干个重载版本,其中之一可以指定输出流以及设置图片格式。本示例就是使用了这个函数。
【应用】

Html中代码(验证码部分,局部):
<h1> 验证码 </h1> <script> function ChangeSD() { document.getElementById("imgSD").src = ""; document.getElementById("imgSD").src = "/ValidationCode.ashx?reqNum=10"; }; </script> <img src="/ValidationCode.ashx?reqNum=10" id="imgSD" /> <input type="button" value="Change Validation Key" onclick="ChangeSD()" />
注意,之所以使用js设置img的src两次,是因为重复的路径不会引发请求。
