|
|
2009年4月8日
从silverlight 1开始,MS对这个技术的定位似乎更重视于互联网应用的娱乐性体验,但是,我认为silverlight技术应该更多关注一下企业应用的解决方案。
把html应用于企业应用的原始动力是易于维护和部署,但是由于html的设计先天上就只是为了内容的展示而非交互,因此在实现企业应用中常有的复杂逻辑、界面逻辑控制方面根本就是草率应付,虽然后来加入了JS来扩展应用,但是基础html规范的简陋,使得即使我们只是想要实现一个限定类型的输入框,也不得复制一堆堆繁琐而丑陋的代码。
另外一个问题是http协议的无状态性。功能提交后的新页面实际上与之前的页面之前没有任何关系,即使你只是刚刚离开了0.1秒,服务器都不会记得你是谁,你之前有没有来过,你的上下文信息等。可以说,WEB应用的开发,有一半努力都是在围绕如何解决这个问题而展开,session, cookies, viewstat, hiddeninput, url string ... 虽然通过这些或高明或不高明的手段,会话上下文信息可以得以一定程度的延续,但是比起CS架构的程序中上下文的自然持续来,WEB程序为解决这种问题而付出的努力、进而造成的资源浪费,真得是让人无法接受。
http协议的无状态性带来的另一个问题是很难实现WEB事务,而操作的一致性却正是企业应用中所最重视的。用户可以随便地在页面之间中断,关闭,回退,前行,把界面控制逻辑搞得无所适从,一团乱麻,真是企业应用的恶梦。
http的这种设计,是为了提高服务器的性能容量而,以及受限于协议开发时的技术能力,而做出的妥协,html亦然。因此,web程序极合适于开发信息发布系统,而不宜于开发逻辑要求、界面控制要求较高的企业应用。
silverlight来自于AcitiveX\jave applet等这种嵌入式程序的思想,但是MS用新的.net技术和为这种思想进行了重新再造,虽然是新瓶装旧酒,但是有可能新一举解决部署和功能两方面的问题,即有web程序的易于部署和维护,又有客户端程序的易于开发,加上严格的UI行为控制,加强的安全性策略,以及更先进的通信能力,silverlight会成为下一代企业应用的主流解决方案。
但是,silverlight似乎把解决企业应用问题放在了比较低的优先级上:1.0时,不支持强类型的.net语言开发,到2.0时,.net语言功能的加入使得已经可以在企业应用场合中试着做一些尝试了。但是,很多功能还只是简化版,无法完全满足要求,而企业应用中比较不重视的娱乐、多媒体等功能却得到了大力的增强。
这与MS一向忽视企业用户的传统一脉相承,同一个原因让MS忽视过internet开发,忽视过数据库,忽视过服务器产品,忽视过服务应用开发。也正是同一个原因,使.net这个优秀平台在进入很多大型企业应用的核心地带时多遇阻碍。但是好在MS总是能及时醒悟,通过强大的技术能力收复失地。IE来了,SQLSERVER来了,.net来了,现在,silverlight也来了,但是,和MS的一贯作风一样,silverlight首先解决面向个人客户的问题。
我对silverlight这个技术报以最热忱的希望,因为它把应用开发、逻辑控制、与部署维护等几个要素调和在同一种技术中,提供了目前为止最好的解决方案,因此,silverlight的架构非常合适于企业应用的开发。希望silverlight能在后续的发展中,能在解决富UI体验的同时,再为企业应用方案方面提供更多支持。
可喜的时,在silverlight 3.0的改进中,果然已经包括了一些对企业应用支持的增强。 以下内容摘自:http://silverlight.net/forums/p/23990/87544.aspx Richer text control 富文本框 Support for spell checking 语法检查 Support for inline tables 行内表格 Support for PDF PDF文档支持! HTML rendering html生成。 Printing 打印!! Offline storage 离线保存!! Local database storage 本地数据库!! Database synchronization 数据同步!! Can install Silverlight application on desktop 可以把应用安装在桌面上!! Access to local hard drive limited to MyDocuments 可以访问本地硬盘上的“文档” !!! Access to all of hard drive 可以访问本地硬盘!!! Can drag & drop from desktop to Silverlight 可以从桌面上拖拉对象到silverlight!! Ability to create new Silverlight windows 可以创建新窗口!! 是不是指对话窗那样的模态窗口? Support for device access such as USB 可以访问本地设备!! Hardware accelerated graphical effects 硬件图形加速。 Blend modes 融合(?)模式 Frame based animation 帧动画 Inverse kinematics / bones 反向动力与骨架! 3D 3维动画
(但是我仔细看过之后,似乎上述的内容比较时久,而可能与真正的silverlight3不同!)
接下来我们还需要什么? http上下文的访问能力。 服务器session对象访问能力。 更方便地访问宿主页的viewstat。 根据安全设置, 可完全访问web service。 模态对话窗。 应用Closeing, Closed,Unload事件支持。 一个服务器数据源映射层,透明提供对服务器数据源的操作功能? 事务支持。 ...
2008年7月18日
只是一个简单的测试, 用了三种方法来做10万条记录的插入. 不过我没有测试拼10万条sql插入的效率.
应用在:dell inspiron 640M本本上跑的. 1.60双核CPU, 2G内存. 服务器是HP360, 8CPU, 4G内存. SQL2000数据库.
仅供参考.
一.每次都调用ctx的InsertOnSubmit方法把对象放入:
2008-7-18 14:29:55 开始生成100000个对象. 并直接放入ctx; 用时4秒
2008-7-18 14:29:59 开始SubmitChanges; 用时3分26秒
2008-7-18 14:33:25 完成.
运行时内存占用了4M左右.
二. 先生成一个list, 最后把list调用ctx的InsertAllOnSubmit方法把全部对象放入:
2008-7-18 18:45:06 开始生成100000个对象. 并放入list 用时1秒
2008-7-18 18:45:07 开始把list全部对象加入ctx. 用时2秒
2008-7-18 18:45:09 开始执行submitChange 用时3分33秒. 应该是数据库的原因导致的慢了7秒. 个人认为不重要.
2008-7-18 18:48:42 完成.
运行时内存占用了4M左右.
三.每生成一个记录都调用ctx的insertOnSubmit, 并且都立即执行submitChanges. 每5000个报一次.
2008-7-18 19:41:28
2008-7-18 19:41:29 0
2008-7-18 19:42:08 5000 此5000条用 39 秒
2008-7-18 19:43:39 10000 此5000条用 91 秒 多了52
2008-7-18 19:46:02 15000 此5000条用143 秒 多了52
2008-7-18 19:49:17 20000 此5000条用195 秒 多了52
2008-7-18 19:53:25 25000 此5000条用248 秒 多了53
2008-7-18 19:58:29 30000 此5000条用304 秒 多了56
2008-7-18 20:04:28 35000 此5000条用359 秒 多了55
2008-7-18 20:11:22 40000 此5000条用414 秒 多了55
2008-7-18 20:19:14 45000 此5000条用472 秒 多了58
2008-7-18 20:28:05 50000 此5000条用531 秒 多了59
2008-7-18 20:37:50 55000 此5000条用585 秒 多了54
2008-7-18 20:48:35 60000 此5000条用645 秒 多了60
2008-7-18 21:00:38 65000 此5000条用723 秒 多了78
2008-7-18 21:13:01 70000 此5000条用743 秒 多了20
2008-7-18 21:26:01 75000 此5000条用780 秒 多了37
.... 等不及了, 关了程序了.
运行时内存占用了4M左右.
越来越慢.
2008年7月17日
摘要: 多年了,没有什么技术再能让我激动得无法控制住自己,包括WPF的超炫界面功能,我也只是小小地喜悦了一下,但毕竟知道界面设计更多还是美工创意的功夫,见过很多品味不够的开发人员,把自己程序界面搞得花里胡骚,简直得像不小心坐在调色板上的大熊的屁股,只能让人笑话。所以WPF的UI能力似乎离自己还有距离。
但是,Linq,却真得把我雷到了。在我重新坐下来写这段话之前,我是激动得在屋里转了几圈的,嘴里至少把MS骂了十来句“WC!”,这骂不是怀了恨意的骂,而是怀了敬意的骂,是没办法控制自己兴奋和激动的心情的骂,就像是《朱罗纪公园》里的马尔康姆教授第一眼看到人工DNA造出来的活恐龙时,说的那句话:“这帮狗娘养的真的做了!”
阅读全文
2005年11月14日
在MSDN上闲逛, 无意中看到一个这样的东西: 屏幕逻辑集成.
SLI(屏幕逻辑集成,Screen Logic Integration)用于将古老的UNIX绿色终端字符界面的程序包装成为一个web services, 以便在SOA的架构里重用这些古老的业务逻辑.
比如, 在我们公司中, 业务系统是基于UNIX的终端程序, 一些业务逻辑是在界面上的一些输入框里输入参数, 一些资料, 或是计算结果被显示在屏幕上的一个地方, 这些业务逻辑通过NEON Systems ServiceBuilder可以以web services的方式发布. 其过程如下:
1.在VS中新建一个NEON Systems ServiceBuilder的项目以建立一个SLI方案.(当然, 前提是你得先安装了这个东西). 2.向导提供一个录制器, 用来记录你的telnet程序的输入和输出, 记录下屏幕上的所有的输入了信息的地方和输出了信息的地方. 3.你在向导的录制结果中, 以直观的方式指定哪些是输入参数, 那些区域是输出结果. 还有数据类型等. 4.向导生成有关的对象模型, 生成webservice. 实际运行时呢? 我猜是这样的: 1. web services被调用, 参数被传入. webservices程序调用neon的程序. 2. NEON偷偷以telnet协议连入unix主机, 按录下的按键序列调用unix主机上的程序. 3. 参数在指定的地方由程序摸拟输入. 4. 主机程序回应结果, 这些结果本来是要显示在屏幕上的, 现在被NEON得到, 根据指定的位置信息和这些信息对应, 分辩出哪些是什么字段, 返回给web services程序. 5. web services向调用者回应结果对象.
这个东西真得有用. :D
MSDN上的文章在这里: http://www.microsoft.com/china/MSDN/library/KnowledgeBase/kb_0409.aspx
2005年9月16日
项目组的文档风格问题
叫我怎么说?我们项目组里现在充斥着华而不实的文档作风. 做一件事,这样浮燥,这样不脚踏实地是不行的.
写工作文档,规范严格是重要的,但并不是要把简单问题往复杂了搞,不是要把实际问题抽象化,不是要去找一些意义含糊的词语来表达,不是要把一句话可以说明白的意思用一大段话来说得人人都看不懂!
说到工作文档,我想有下面几点要做好,就够了.
一.句法不能出问题:
- 主谓宾一个也不能少.
- 如果主谓宾的词语不能确切地,无岐义地表达事实,那么必须用确切的定语\状语\补语成分.
- 不推荐应用定状补定子句.如果需要的定状补成分是一个子句,那么尽可能分句,在另一句中对于需要限制定的语素进行详细说明.
- 标点符号不能出错,括号内外的标点要遵守括号标点的惯例.
二.用词:
用词的原则为:
- 能简单不复杂.
- 能用常用字词表达,则不用冷僻词.
- 能用有确切含义的技术名词,则不用俗称或是其他非技术名词.
- 能具体不抽象.例如要传达"椅子"的概念,就用"椅子"这个词,而不是采用"泛化单式坐恣人体支撑家俱"这样的词,虽然然这样说起来显得很高档.
- 英文缩写词提供术语解释.
- 有数值指标的,则不用比较级形容词,如"数据库的容量要达到非常大的容量,在大容量下的性能要在可容忍的限度内."这样的话实际上没有传达信息.而"数据库设计要支持1亿条记录的容量,同在达到1亿条数据时,用身份证号查询记录时,得到结果的时间最长不能大于0.1秒"这样的话就精确定义了信息.
看来我们项目组里有些人需要的不是专业技能, 而是需要补习语文!
2005年9月5日
求助: VC++ 中 DLL编译时结构体成员对齐的问题
现在我们有一个需求, 要求我们用VC++编写一个DLL, 叫GT2MQ.dll, 输出一些函数供一种叫GRAPHTALK(GT)的语言使用, 在这些函数的实现里调用了另一个其他厂商提供的中件间产品的DLL函数(adapter.dll).
GT对DLL函数的调用是有要求的, 其中最重要的是必须在编译时指定结构成员按1字节对齐, 否则GT在调用这个DLL的函数时会出现非法操作.
在GT2MQ里的方法调用adapter.dll的一个方法时, 需要传入一个结构体的指针做为参数, 这个结构体的定定义如下: /*消息结构*/ typedef struct BusMessage { char messageId[50]; char correlId[50]; char appMessageId[30]; char version[30]; char bodyType[20]; char bodyCategory[20]; char timeStampCreated[30]; char timeStampExpired[30]; char srcLogicalId[30]; char dstLogicalId[30]; char authenticationId[30]; char commandMode[30]; char txnScope[30]; char *standardBody; char *body; char priority[20]; char persistence[10]; char expiry[10]; char traceLevel[10]; char publish[10]; char backup[10]; char messageName[384]; char encoding[30]; char msgCharset[30];
long msglen;/*发送消息的长度*/ long rcvlen;/*接收消息需要的内存*/ }BusMessage, *ptrBusMessage;
当我们的DLL函数新建了这个结构体, 清零, 并填写了必要的字段后, 把结构的指针做为参数调用adapter.dll中的一个函数, 然而这时后发现无论如何, 这个方法调用都不成功. 返回的错误是说msglen的字段没有填值. 但那个字段明明是填入了值的.
由于adapter.dll没有源码, 只好通过汇编代码来debug, 才发现这个结构体在我们的dll里和在adapter.dll里对字段寻址时地址不一样, 我们的DLL对msglen的寻址是msg+932, 而adapter.dll对同一个字段的寻址是msg+936!
通过手工计算, 确定msglen成员的偏移值应取932, 但是如是果同一个结构体定义放在一个新建的win32工程中时, 查看汇编代码, 就发现是按936寻址的.
后来再进一步查找, 发现问题出在下面两个字段的定址上:standardBody和msglen, 这两个字段在定址时都向后移动了两个字节, 造成了一共四个字节的偏移, 这使得成员访问数据时出错.
我们怀疑是由于我们的GT2MQ.dll项目里的那个结构成员对齐选项设为"1字节对齐"(/Zp1)造成的, 但是无论我们把这个参数设为多少, 都不会消除这个问题:我们的DLL始终不会为那两个字段增加两个字节的偏移. 何况我们项目的要求是GT2MQ.dll必须指定/Zp1选项, 即使这样解决了也无法满足要求.
我们目前的解决办法是: 在结构体里在standardBody和msglen之前各增加了一个字段: char Reserved1[2]和char Reserved2[2]来强迫我们的DLL为之后的成员定址时加一. 但是感觉这样做并不是很妥当, 必竟我们修改了厂商提供的头文件, 而同一头文件在其他项目里是不会出现问题的.
诚求更好的解决方案, 或是知道如何调整项目设置的高手请赐教!
2005年8月31日
一定得选最时摩的外国系统. 雇法国公司? 搞就搞最 high level 的方法论. IAA模型直接用上. SOW最少也得整上三四百页. 什么再保呀,精算呀,两核呀, 银保呀. 能整的全给他划进scope里去. 这边搞个技术组, 那边来个业务组. 办公室门口站一白人经理. 打领带, 笑容特虚伪的那种. 打工的一进门, 甭管是写程序的还是搞需求的都得跟经理打招呼: morning, 我的output昨天已经加班derived了. 打心里头没底的腔儿. 倍儿没面子. 项目组里再设个资深专家团. 方法论用RUP的. 一天光workshop就是十多个小时. 再请一个美国IBM的顾问, 每天就来工作一个小时. 就是一个字儿----贵. 光确定一个项目组人员结构就要好几十万的. 周围其他项目组不是迭代式实施就是敏捷式开发. 你要是一张口只会讲OOAD呀. 你都不好意思和人家打招呼! 你说这样的项目, 得多少时间上线? 我觉得怎么着也得大后年吧! 大后年? 那只够完成需求分析的. DAY1定在奥运会后! 你别嫌慢, 这已经是简化的process了. 你得研究甲方的心理. 肯出几个亿做project的, 在乎的就是过程. 这什么叫样板工程? 你知道吗? 样板工程就是不管搞什么project, 都用最考究的方法, 不用最直接的方法. 所以, 我们做IT项目的口号就是 不求能出活, 但求最规范.
2005年7月28日
摘要: 1.1. Windows界面设计标准1.1.1. 易用性l 界面元素的名称、标签应该易懂,用词准确,避免使用模楞两可的字眼,要与同一界面上的其他元素易于区分,能望文知意最好。理想的情况是用户不用查阅帮助就能知道该界面的功能并进行相关的正确操作作。 l disable而不是not visible。l 完成相同或相近功能的按钮用GoupBox框起来,常用按钮要支持快捷方式。 l 完成同一功能或任务的元... 阅读全文
2005年6月21日
经过无数次的重装, 删除, 终于明白了, 必须要先装informix cli 2.82(这是唯一一个可以和oracle 9.2i共存的informix cli 32 版本), 再装oracle 9.2i cli, 才可以同时访问两种数据库的odbc 或oledb的连接, 不然的话就会在新建oracle92的 ODBC或是OLEDB连接时出现找不到oracle驱动程序的问题.
切记切记!
2005年6月5日
同一个输出流, 不可以在多线程上共用. 如果要共用, 一定要使用临界段. 不同的输出流, 在多线程上并发处理时互相没有影响. 代码说话:
public static void testmulttts() { Thread [] arT = new Thread[8]; for (int i = 0; i < arT.Length; i ++) { arT[i] = new Thread(new ThreadStart(testtts)); }
foreach(Thread t in arT) { t.Start(); Thread.Sleep(500); System.Console.WriteLine("线程已经启动!"); } }
public static void testtts() {
///http://msdn.microsoft.com/library/default.asp?url=/library/en-us/SAPI51sr/html/ispvoice_speak.asp SpeechLib.SpVoiceClass sp = new SpeechLib.SpVoiceClass();
SpeechLib.SpFileStreamClass fs = new SpeechLib.SpFileStreamClass(); fs.Format.Type = SpeechLib.SpeechAudioFormatType.SAFT8kHz8BitMono; fs.Open("c:\\" + Guid.NewGuid().ToString() + ".wav" , SpeechLib.SpeechStreamFileMode.SSFMCreateForWrite, false); // sp.AudioOutputStream = fs; // The format of selection criteria is //"Attribute = Value" and "Attribute != Value." // Voice attributes include //"Gender," "Age," "Name," "Language," and "Vendor." // 上述这些属性的值可以从注册表的 //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MSSimplifiedChineseVoice\Attributes // 看到 SpeechLib.ISpeechObjectTokens sps = sp.GetVoices("Language = 409", ""); for (int i = 0; i < sps.Count; i ++) { sp.Voice = sps.Item(i);
lock(typeof(Class1)) { sp.Speak("speak in English.", SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault); } } sps = sp.GetVoices("Language = 804", ""); if (sps.Count > 0) { sp.Voice = sps.Item(0); lock(typeof(Class1)) { sp.Speak("中华人民共和国, 中央人民政府, 成立了!", SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault); } } fs.Close(); Marshal.ReleaseComObject(sp); Console.WriteLine("线程录音完成."); } }
2005年6月4日
先决条件:
装了office2000或更新版本中带的MS语音输入法. 这可能就是MS中国研究院的成果之一. 有语音输入功能和语音拼读功能.
下载ms speech api sdk 5.0或5.1, 如果只用C#开发, 则不用装这个, 如果希望用C++开发, 加装语音库之类的, 不妨也装这个好了. 装了这个, 第一条的也就包含了.
C#工程里加上对com对象"Microsoft Speech API 5.0"的引用. Interop而已.
不再多说, 只贴个代码, 以备日后参考:
public static void testtts() { //参考文档的起始链接在: //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/SAPI51sr/html/ispvoice_speak.asp
// 引擎COM对象. SpeechLib.SpVoiceClass sp = new SpeechLib.SpVoiceClass(); // 朗读的内容到一个文件流里去. SpeechLib.SpFileStreamClass fs = new SpeechLib.SpFileStreamClass(); //设定流的格式, 48kHZ, 16Bit, 立体声. 如果是电话音质, 8K8bit单声道就可以了. fs.Format.Type = SpeechLib.SpeechAudioFormatType.SAFT48kHz16BitStereo; fs.Open("c:\\test.wav", SpeechLib.SpeechStreamFileMode.SSFMCreateForWrite, false); sp.AudioOutputStream = fs; // 如果不指定这个内容, 那就会在计算机的声卡上放出来. // The format of selection criteria is //"Attribute = Value" and "Attribute != Value." // Voice attributes include //"Gender," "Age," "Name," "Language," and "Vendor." // 上述这些属性的值可以从注册表的 //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MSSimplifiedChineseVoice\Attributes // 看到
// 得到属性符合指定要求的所有的拼读token. "Language = 409"表示得到所有的英文拼读器. 等号两边的空格不可少! // 如果有多个过滤条件要指定, 中间用分号分开, 如要指定英文女声, 则为 // "Language = 409; Gender = Female" SpeechLib.ISpeechObjectTokens sps = sp.GetVoices("Language = 409", ""); // 可能有多个拼读器. 咱们每个都用一遍, 是不同的嗓音, 有男有女. for (int i = 0; i < sps.Count; i ++) { sp.Voice = sps.Item(i); //这里这个圆括号不是我写错了, 就是这样的.这是一个方法,不是索引器.
sp.Speak("User experience and interface design in the context of creating software represents an approach that puts the user, rather than the system, at the center of the process.", SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault); // 可以用标志位指定异步调用这个方法. }
// 得到中文拼读器, 因为目前只有一个, 也就不用for了. sps = sp.GetVoices("Language = 804", ""); if (sps.Count > 0) { sp.Voice = sps.Item(0); sp.Speak("中华人民共和国, 中央人民政府, 成立了!.", SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault); } fs.Close(); Marshal.ReleaseComObject(sp); }
2005年6月2日
问题一: 自已写到一个类, 其中有一个属性是字串型, 是用来保存一个文件名的, 这个类需要客户在使用时能在PropertyGrid里runtime修改内容, 友好的方式当然是让客户在PropertyGrid里可以有一个"..."的按钮, 点一下之后打开一个openfiledialog, 选择一个文件之后返回, 文件的全路径就放在属性值的框里了. 如何达到这个目的? 比如这个类如下:
public class class1
  {
public class1()
 {
}
// 

private string _s;
public string s
 {
 get {return this._s;}
 set {this._s = value;}
}
}
 这样的话在PropertyGrid里s属性的后面只有一个简简单单的编辑框, 为了使s的编辑区可以出现一个"..." 按钮, 并且可以用文件打开对话框选择文件, 可以使用EditorAttribute属性对这个属性进行标记:

[Editor(typeof(System.Windows.Forms.Design.FileNameEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public string S
  {
 get {return this._s;}
 set {this._s = value;}
}
 FileNameEditor类提供了一个打开文件的对话框, 以编辑属性值, 这个类派生于UITypeEditor类, UITypeEditor是所有设计时界面的基类. 这样就可以达到有文件对话框的目的了, 但是如果想要指定只选择某一种文件类型, 如"WAV"文件, 那就得派生FileNameEditor, 重写有关的方法.
public class SoundFileEditor:System.Windows.Forms.Design.FileNameEditor { protected override void InitializeDialog(OpenFileDialog openFileDialog) { base.InitializeDialog (openFileDialog); // 在基类初始代完对话框之后, 可以对这个对话框做一些手脚. openFileDialog.Filter = "wav and vox file(*.wav, *.vox)|*.wav;*.vox|wav files (*.wav)|*.wav|vox files (*.vox)|*.vox|All files (*.*)|*.*"; } }
再把Class1中的S的EditorAttribute改为派生的这个子类, 现在对话框里的文件过滤器改为你想要的了. 你甚至可以从UITypeEditor自己重新派生一个子类出来, 自己写一个能弹出"保存文件"对话框的UI编辑类. 注意在重写UITypeEditor的
public virtual new System.Object EditValue ( System.ComponentModel.ITypeDescriptorContext context , System.IServiceProvider provider , System.Object value ) 方法, 注意返回自己需要对象就可以了. 问题二: 如何设定一个类的"默认值编辑器"? 例如: 比如有一个类叫SoundFileName专门用来处理声音文件名, 如果Class1中有一个属性FileName是SoundFileName类型, 这个属性在PropertyGrid里编辑器里默认情况下是没有办法编辑的, 为了让SoundFileName类可以在propertyGrid里可以编辑, 必须对SoundFileName进行属性标志, 指定一个默认的编辑器. 假定我们还是希望通过"打开文件"对话框来指定一个文件的方式生成一个SoundFileName对象, 那么我们可以写一个从UITypeEditor中直接或间接派生的子类, 以完成编辑对象内容的操作. 如下代码所示, 这个专门编辑SoundFileName对象的类叫SoundfileNameEditor, 我们随后实现它, 现在先看SoundFileName的实现. 注意class之前的标记:
[EditorAttribute(typeof(SoundFileNameEitor), typeof (System.Drawing.Design.UITypeEditor))] public class SoundFileName { private string _filename; public string FileName { get{return _filename;} }
public SoundFileName(string s) { this._filename = s; } }
这里定义了此类的值编辑器为SoundFileNameEditor类, 这个类我们需要从UITypeEditor或是其子类中派生出来, 重写其EditValue方法, 以可以返回一个SoundFileName对象. 为了省事, 我就不再从UITypeEditor派生了, 而是从SoundFileEditor派生, SoundFileEditor已经把文件名过滤器修改了, 但是SoundFileEditor的EditValue返回的是一个字串, 这次只要修改SoundFileEditor的EditValue的值就可以了.
public class SoundFileNameEditor: SoundFileEditor { public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { SoundFileName sfn = value as SoundFileName; return new SoundFileName(base.EditValue (context, provider, sfn==null?"":sfn.FileName) as string); } }
注意, 这个EditValue总是会返回一个新的对象, 把从前的对象扔掉, 你可以通过判断value是不是null来决定是否新建一个对象, 还是只是修正一下其中的文件名, 这里不再多说了. 现在你在PropertyGrid里可以编辑SoundFileNmae对象了, 编辑器弹出一个打开文件对话框, 选择了文件之后, 根据文件名建立了一个SoundFileName对象. 最后提示一下, 不知道你注意到了没有, 当选择了文件名建立了一个SoundFileName对象之后, 在编辑框里的内容是"testanything.SoundFileName", 用户看起来不会太高兴, 也不直观, 这个也好办, 你可以重写SoundFileName的ToString()方法来用任何方式显示你想要内容.
2005年5月12日
process早就有办法了, 只不过我没有注意.
如调用一个命令行:"cmd /c dir c:\winnt"; 把结果放到一个字符串里.
ProcessStartInfo psi = new ProcessStartInfo("cmd", " /c dir c:\winnt");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
注意那个waitforexit()一定要放到readtoend之后. MSDN里这样说:
process组件通过管道与子进程通信。如果子进程写入管道的数据多得足以填满缓冲区,则子进程将一直会阻塞到父进程从管道读取数据时为止。如果应用程序将所有输出读取到标准错误和标准输出,则这会导致死锁。
意思就是: 如果waitforexit在前, 那么如果数据太多而超出缓冲期大小, 子程序就会等着消费者把数据读出来, 可是这时候由于waitforexit正在阻塞, 做为消费者的过程序readtoend无法执行, 因而导致了死锁.
除了上面的, 还可以用程序交互:
ProcessStartInfo psi = new ProcessStartInfo("cmd");
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
p.StandardInput.WriteLine(@"dir c:\winnt");
p.StandardInput.WriteLine(@"ver");
p.StandardInput.WriteLine(@"exit");
output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
重定义了stdin, 执行cmd程序, 进入了命令行交互, 然后向标准输入里写若干个命令, 回车, 有意思, 执行了! 最后千万别忘记了用"exit"命令中断cmd的执行, 不然后面的readtoend时, 永远也不会读到END, 程序又阻塞在这里不会退出.
最后, MSDN提到了standarderror, 如果同时重定向了stdout和stderr, 那么不当的方式也会出现死锁: 如:
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
MSDN说程序先读了stdout, 完了之后再读stderr, 如果子进程在执行时向stderr写了内容, 那么就会死锁. 我想了一会儿, 没想明白. 如果程序在两个流里都写入了, 那么第一个读stdout的动作完成之前, stderror的内容留着不就行了? 但是没有时间试了.
MSDN建议用两个线程来处理不同重定向流. 这是个好办法.
2005年4月27日
寄来了门票, "2005年中国IT发展动向", 上印票价500. 是么! 看来是要去听了, 500元, 好值钱呀! 不要钱就送来了?
地址在北京饭店, 去时已经是10:30, 交了票, 一问, 什么都没有, 资料也发完了, 午饭票也发完了, 进场看看, 座位也坐完了. 还有人在离开, 有人在进来, 有人在出去. 来来往往, 后面还有人在卖书, 1元一本的PC世界, 有人在买, 有人在卖音箱, 有人在卖USB台灯, 哦, 不是卖的, 是在送, 只要订一年的杂志就送一个. 还有人在卖煎饼果子...哦不是, 我早上没吃饭, 饿得有点幻觉.
下面的人在赶集, 上面有个人正在有一句没一句地讲, 讲什么呢? 从前有座山... 不对不对, 他在讲"整合驱动创新", 我还真是不知道这是什么意思. 似乎都是很流行的词, 我觉得自己回到了从前学英语时的那种场景, 每个词都认得, 就是不知道放到一起是什么意思. 我索性到第一排去听, 第一排都有水喝, 后面的就没有了. 我站在一边上听, 那人说是什么大中华地区总裁什么的. 正在讲一些PPT上的内容. PPT上的内容我也看不懂, 上一句和下一句也没有什么联系, 大纲和小标题之间也看来出有什么联系. 没有联系得我现在一句话也想不起来.
然后就听他讲, 他突然抱怨"这个屏幕可能有什么问题", 他的幻灯片里的曲线是该向上的, 那该死的屏幕把它显示错了, 显示成向右的水平线了. 我开始考虑是不是显示驱动有一些问题, 在DX状态下PPT播放时会不会造成一些不兼容, 那个视频线会不会中途CUAN改他PPT的内容, 都排除了之后我才意识到这个说法很高雅, 我一定得学会这一招, 在合适的时候认为自己的屏幕把内容给显示错了.
之后的内容我还是不大明白, 他又说了一些零库存的事儿, 后来他终于说到SRA, 面向服务架构企业应用什么的, 我开始有点精神了, 总算知道说到哪儿了. 他说互联网就是最大的异构化的服务体系, 要把这些异构的同构起来. 我更有精神了. 我想听听他有什么好办法把这玩意儿同构起来.
可是他又不说如何同构, 只说整合驱动创新. 原来这三个词连到一起的意思是"整合了, 就会驱动IT人员去驱新", 然后又峰回路转, 说选用什么中间件很重要. 我正在想这么不知云的会议靠什么钱来支持, 听到这个话就有点明白了, 翻了翻日程表, 这个讲者是IBM的大中华总经理什么的, 然后就听他说他们IBM的中间件很好.
我正等着他讲IBM的中间件, 他的PPT突然说谢谢大家. 我以为他会再骂一次他的屏幕乱改他的内容, 可是他下来了. 主持人上去报幕说下一讲是谈EPSON的"微墨滴技术构建企业打印平台". 我看看下午的议程, 有"构建64位计算平台", "高性能计算的平民化趋势", "网络架构下企业应用中的UPS不间断电源保证"什么的. 还有观众抽奖, 第二次抽奖....
这时一个日本人上去, 说"我给你起哇". 然后开始哇拉哇拉讲.
我饿极了, 就走了.
2005年4月19日
花了155元.
早有这个打算, 一直要考试, 学习等, 不敢买, 上周终于考试完了, 立即下手买回来.
昨天看到凌晨一点. 放不下了.
考试真烦, 明明答得不错, 可是还要担心不及格, 只因为老师想让不及格就可以给个50多分. 上次也答得不错, 可是具然还是50多分, 知情者说是老师嫌我们没有孝敬他, 故意把及格的都拉下来了. 50多分的就是已经及格的. 这社会, 满是邪恶.
这个卡和dialogic看来还是有一些差距的, 只支持8k8bit的声音文间, 其他的总是会出现一些奇怪的事情.
2005年3月11日
如何调用方法?本示例阐释如何通过反射调用各种方法。由于所调用方法的名称存储在字符串中,因此该机制提供在运行时(而不是在设计时)指定要调用的方法的功能,提供了使您的用户可以控制调用哪个特定方法的余地。尽管本演示集中于调用方法,如果需要您还可以设置和获取属性和字段。有关本主题的另一个实例示教,请参阅 如何使用数学函数主题下的示例。
在许多代码方案中,在执行任务以前您知道要实现的任务。因此,您可以指定需要调用的方法以及需要传递给它们的参数。但是,还有一些情况下您可能希望根据特定方案或用户操作动态调用方法。该功能可通过 Reflection 命名空间使用,方法是使用 Type 对象上的 InvokeMember 方法。
您还可以进行其他操作,如获取或设置指定属性的值。这些操作可通过 BindingFlags 枚举使用。InvokeMethod 的第二个参数是您指定的 BindingFlags 操作的组合。例如,如果想调用某个类上的静态方法,可以在 BindingFlags 和 InvokeMethod BindingFlag 中包括该静态元素。下面的示例展示如何调用名为 SayHello 的假想方法,其中 SayHello 是静态方法。
// calling a static method, receiving no arguments
// don't forget that we are using object in the reflection namespace...
using System;
using System.Reflection;
public class Invoke {
public static void Main (String [] cmdargs) {
// Declare a type object, used to call our InvokeMember method...
Type t = typeof (TestClass);
// BindingFlags has three bitor'ed elements. Default indicates
// that default binding rules should be applied.
t.InvokeMember ("SayHello",
BindingFlags.Default | BindingFlags.InvokeMethod
| BindingFlags.Static, null,
null, new object [] {});
}
}
' calling a static method, receiving no arguments
' don't forget that we are using object in the reflection namespace...
Imports System
Imports System.Reflection
Public Class Invoke
Public Shared Sub Main ()
' Declare a type object, used to call our InvokeMember method...
Dim t As Type = GetType (TestClass)
' BindingFlags has three bitor'ed elements. Default indicates that
' default binding rules should be applied.
t.InvokeMember ("SayHello", _
BindingFlags.Default BitOr BindingFlags.InvokeMethod _
BitOr BindingFlags.Static, nothing, _
nothing, new object () {})
End Sub
End Class
|
| C#
|
VB
|
|
快速查看一下传递给 Invoke 方法的其余参数。传递的第一个空参数请求使用默认联编程序绑定正在调用的方法。当调用默认联编程序时,请包含默认的 BindingFlags。第三个参数可以不为空,您可以指定一个 Binder 对象,它定义一组属性并启用绑定,这可能涉及选择重载方法或强制参数类型。第二个空参数是您在其上调用所选方法的对象。最后,传递由成员接收的参数对象数组。在本例中,SayHello 方法不接收任何参数,因此传递一个空数组。
下面的情况略有不同。调用名为 ComputeSum 的另一个静态方法,但是在此情况下,此方法需要两个参数。因此,用这些参数填充一个对象数组,并将它们作为最后一个参数传递到 InvokeMember 中。
// Calling a static method, which needs arguments
object [] args = new object [] {100.09, 184.45};
// we know that this particular method returns a value, being the computed sum,
// so we create a variable to hold the return
// note the datatype of the return is object, the only datatype InvokeMethod returns...
object result;
// invoke the method. Note the change in the last parameter: the array we populated...
result = t.InvokeMember ("ComputeSum", BindingFlags.Default | _
BindingFlags.InvokeMethod | BindingFlags.Static,
null, null, args);
// write the results to the user's console...
Console.WriteLine ("{0} + {1} = {2}", args[0], args[1], result);
' Calling a static method, which needs arguments
Dim args as object ()
args = new object () {100.09, 184.45}
' we know that this particular method returns a value, being the computed sum,
' so we create a variable to hold the return
' note the datatype of the return is object, the only datatype InvokeMethod returns...
Dim result As object
' invoke the method. Note the change in the last parameter: the array we populated...
result = t.InvokeMember ("ComputeSum", BindingFlags.Default BitOr _
BindingFlags.InvokeMethod BitOr BindingFlags.Static, _
nothing, nothing, args)
' write the results to the user's console...
Console.WriteLine ("{0} + {1} = {2}", args(0), args(1), result)
|
| C#
|
VB
|
|
在前两个示例中,已调用了静态方法。还可以调用实例方法。若要这样做,将您要在其上调用方法的类型的对象作为第三个参数传递。本示例还展示为了使用 InvokeMember,您不必有实际的 Type 对象。在此情况下,通常将希望使用所拥有的类实例来调用 GetType,如下面的示例所示。注意由于未调用静态方法,所以 BindingFlags 已更改。
// Calling an instance method
// we need an object reference to invoke an instance member
TestClass c = new TestClass ();
// use the instance of our class to call GetType
// we no longer include the Static element in BindingFlags for our |
// the fourth parameter is no longer null: we instead pass an instance
// of the object we wish to invoke our method on
c.GetType().InvokeMember ("AddUp", BindingFlags.Default | BindingFlags.InvokeMethod,
null, c, new object [] {});
c.GetType().InvokeMember ("AddUp", BindingFlags.Default | BindingFlags.InvokeMethod,
null, c, new object [] {});
' Calling an instance method
' we need an object reference to invoke an instance member
Dim c as TestClass
c = new TestClass ()
' use the instance of our class to call GetType
' we no longer include the Static element in BindingFlags for our bitor
' the fourth parameter is no longer null: we instead pass an instance
' of the object we wish to invoke our method on
c.GetType().InvokeMember ("AddUp", BindingFlags.Default BitOr BindingFlags.InvokeMethod, _
nothing, c, new object () {})
c.GetType().InvokeMember ("AddUp", BindingFlags.Default BitOr BindingFlags.InvokeMethod, _
nothing, c, new object () {})
|
| C#
|
VB
|
|
有时不想调用方法,而需要调用其他成员,如属性或字段。若要实现它,只需更改 BindingFlags 组合(而不是 InvokeMethod)以包含适当元素即可。下面的示例展示获取和设置字段值。所讨论字段不是静态字段,因此需要创建一个对象实例来请求该字段。设置字段值时,需要将所设置的值作为对象数组参数的唯一元素传递。获取值时,需要将 InvokeMember 方法的返回类型分配给一个对象。
// Setting a field. Assume we are using the same Type and Class declared in the
// previous examples (t and c). The field we are setting is the Name field
// note the BindingFlags argument now includes SetField rather thanInvokeMember
// Further, this is an instance field, so we pass the instance of our class
t.InvokeMember ("Name", BindingFlags.Default | BindingFlags.SetField,
null, c, new object [] {"NewName"});
// similar usage...
result = t.InvokeMember ("Name", BindingFlags.Default | BindingFlags.GetField,
null, c, new object [] {});
Console.WriteLine ("Name == {0}", result);
' Setting a field. Assume we are using the same Type and Class declared in the
' previous examples (t and c). The field we are setting is the Name field
' note the BindingFlags argument now includes SetField rather thanInvokeMember
' Further, this is an instance field, so we pass the instance of our class
t.InvokeMember ("Name", BindingFlags.Default BitOr BindingFlags.SetField, _
nothing, c, new object () {"NewName"})
' similar usage...
result = t.InvokeMember ("Name", BindingFlags.Default BitOr BindingFlags.GetField, _
nothing, c, new object () {})
Console.WriteLine ("Name == {0}", result)
|
| C#
|
VB
|
|
还可以获取和设置属性,但在本示例中,假定所设置属性是一个具有多个元素的数组或集合。若要指定特定元素的设置,您需要指定索引。若要设置属性,请分配 BindingFlags.SetProperty。若要指定属性的集合索引或数组索引,请将要设置元素的索引值放在对象数组的第一个元素中,然后将要设置的值作为第二个元素。若要取回该属性,请将索引作为对象数组中的唯一元素传递,指定 BindingFlags.GetProperty。
// Set an indexed property value
int index = 3;
// specify BindingFlags.SetProperty, and because this is an instance property,
// pass the object to call the property on (c). In the object array, make two elements,
// the first being the index, and the second being the value to set
t.InvokeMember ("Item", BindingFlags.Default |BindingFlags.SetProperty,
null, c, new object [] {index, "NewValue"});
// Get an indexed property value
// specify BindingFlags.GetProperty, and because this is an instance property,
// pass the object to call the property on (c). In the object array, specify the index only
result = t.InvokeMember ("Item", BindingFlags.Default |BindingFlags.GetProperty,
null, c, new object [] {index});
Console.WriteLine ("Item[{0}] == {1}", index, result);
' Set an indexed property value
Dim index As Int32 = 3
' specify BindingFlags.SetProperty, and because this is an instance property,
' pass the object to call the property on (c). In the object array, make two elements,
' the first being the index, and the second being the value to set
t.InvokeMember ("Item", BindingFlags.Default BitOr BindingFlags.SetProperty, _
nothing, c, new object () {index, "NewValue"})
' Get an indexed property value
' specify BindingFlags.GetProperty, and because this is an instance property,
' pass the object to call the property on (c). In the object array, specify the index only
result = t.InvokeMember ("Item", BindingFlags.Default BitOr BindingFlags.GetProperty, _
nothing, c, new object () {index})
Console.WriteLine ("Item[{0}] == {1}", index, result)
|
| C#
|
VB
|
|
还可以使用命名参数,在此情况下需要使用 InvokeMember 方法的另一个重载版本。像迄今一直进行的那样创建对象参数的数组,并创建所传递参数的名称的字符串数组。您要使用的重载方法接受参数名列表作为最后一个参数,并接受要设置的值的列表作为第五个参数。在本演示中,所有其他参数都可以为空(当然前两个除外)。
// Calling a method using named arguments
// the argument array, and the parameter name array. Obviously, you will need
// to determine the names of the parameters in advance
object[] argValues = new object [] {"Mouse", "Micky"};
String [] argNames = new String [] {"lastName", "firstName"};
// the first five parameters for this overloaded method are the same as the
// the five parameters we have used to this point. The final parameter needs to be
// set to the names of the parameters
t.InvokeMember ("PrintName", BindingFlags.Default | BindingFlags.InvokeMethod,
null, null, argValues, null, null, argNames);
' Calling a method using named arguments
' the argument array, and the parameter name array. Obviously, you will need
' to determine the names of the parameters in advance
object[] argValues = new object [] {"Mouse", "Micky"};
String [] argNames = new String [] {"lastName", "firstName"};
' the first five parameters for this overloaded method are the same as the
' the five parameters we have used to this point. The final parameter needs to be
' set to the names of the parameters
t.InvokeMember ("PrintName", BindingFlags.Default BitOr BindingFlags.InvokeMethod, _
nothing, nothing, argValues, nothing, nothing, argNames)
|
| C#
|
VB
|
|
下一个示例展示如何调用类上的默认成员。确保在其上进行调用的类指定有默认成员。然后在 InvokeMember 方法中,不要指定要调用成员的名称,如本示例所示。
// our class with it's default member specified, using the defaultmemeber attribute
[DefaultMemberAttribute ("PrintTime")]
public class TestClass2 {
public void PrintTime () {
Console.WriteLine (DateTime.Now);
}
}
// the client code that uses the above class...
Type t3 = typeof (TestClass2);
t3.InvokeMember ("", BindingFlags.Default |BindingFlags.InvokeMethod,
null, new TestClass2(), new object [] {});
' our class with it's default member specified, using the defaultmemeber attribute
public class TestClass2
public Sub PrintTime ()
Console.WriteLine (DateTime.Now)
End Sub
End Class
' the client code that uses the above class...
Dim t3 As Type
t3 = GetType (TestClass2)
t3.InvokeMember ("", BindingFlags.Default BitOr BindingFlags.InvokeMethod, _
nothing, new TestClass2(), new object () {})
|
| C#
|
VB
|
|
最后一个示例使用略有不同的过程调用方法。不直接使用 Type 对象,而是直接创建一个单独的 MethodInfo 对象来表示将调用的方法。然后调用 MethodInfo 对象上的 Invoke 方法,传递需要在其上调用方法的对象的实例(在要调用实例方法的情况下,但是,如果方法是静态的,则为空)。像以前一样,需要参数的对象数组。如果需要,该特定示例允许您通过引用传递参数。
// Invoking a ByRef member
MethodInfo m = t.GetMethod("Swap");
args = new object[2];
args[0] = 1;
args[1] = 2;
m.Invoke(new TestClass(),args);
Console.WriteLine ("{0}, {1}", args[0], args[1]);
' Invoking a ByRef member
Dim m as MethodInfo =
m = t.GetMethod("Swap")
args = new object() {CObj(1), CObj(2)}
m.Invoke(new TestClass(),args)
Console.WriteLine ("{0}, {1}", args(0), args(1))
|
| C#
|
VB
|
|
如何列出某类型的所有成员本示例使您可以列出给定数据类型的成员。列出类型成员的功能是快速发现哪些元素可用的很好方式。它是在系统中进行报告以及帮助开发用户文档的重要工具。使用 Reflection 命名空间,您可以控制希望显示给用户的成员类型以及其他信息(如特定方法的可见性)。还可以获取类中所有成员的信息,或仅指定某些子集(如方法或字段)。
您可能想知道为何获取特定类型的信息很重要。毕竟,这就是帮助系统和帮助文档的用途,不是吗?下面的示例可以帮助您创建用户文档,或用于帮助动态调用方法或设置属性。
需要执行以下几个步骤。首先,需要获取用户希望使用的类型(以字符串的形式)。确定了要使用的类型后,需要分配一个对象来表示该类型。这将进行两项工作:创建后面步骤可以使用的对象,还确保指定类型存在并且可被系统找到。下面的示例向 System.String 类型分配一个对象。注意,尽管此处示例中通过“控制台”(Console) 对象向用户提供反馈,实际示例却将反馈发送给一个 ASP.NET 标签对象。但解释相同。
// don't forget your using statements at the top of your code...
Using System;
Using System.Reflection;
// class declaration, and method declaration...
// remember that this string is case-sensitive, so be careful
Type t = Type.GetType("System.String");
// check to see if we have a valid value. If our object is null, the type does not exist...
if (t == null) {
// Don't assume that it is a SYSTEM datatype...
Console.WriteLine("Please ensure you specify only valid types in the type field.");
Console.WriteLine("REMEMBER: The Case matters (Byte is not the same as byte).");
return; // don't continue processing
}
' don't forget your imports statements at the top of your code...
Imports System
Imports System.Reflection
' class declaration, and method declaration...
' remember that this string is case-sensitive, so be careful
Dim t As Type = Type.GetType("System.String")
' check to see if we have a valid value. If our object is null, the type does not exist...
If t Is Nothing Then
' Don't assume that it is a SYSTEM datatype...
Console.WriteLine("Please ensure you specify only valid types in the type field.")
Console.WriteLine("REMEMBER: The Case matters (Byte is not the same as byte).")
Exit Sub ' don't continue processing
End If
|
| C#
|
VB
|
|
有了有效的类型对象以后,下一个问题是希望为类型检索什么类型的成员?是需要方法、静态方法还是实例字段?在 Reflection 命名空间中,有一组 Info 对象,每个对象表示您系统的一组不同成员。例如,有一个 MethodInfo 对象可表示有关某方法的信息。还有一个一般 MemberInfo 对象,它表示给定类中可以存在的所有成员。
使用该信息,您可设置以下数组来查看刚刚创建的类型,并弄清类型中有哪种类型的信息。下面示例中的“位”运算符(|符号,或 Visual Basic 中的 BitOr)请求满足指定约束的类型的所有信息。该示例展示如何获取所有字段和所有方法。
// declare and populate the arrays to hold the information...
FieldInfo [] fi = t.GetFields (BindingFlags.Static |
BindingFlags.NonPublic | BindingFlags.Public); // fields
MethodInfo [] mi = t.GetMethods (BindingFlags.Static |
BindingFlags.NonPublic | BindingFlags.Public); // methods
' declare and populate the arrays to hold the information...
Dim fi() As FieldInfo = t.GetFields(BindingFlags.Static BitOr _
BindingFlags.NonPublic BitOr BindingFlags.Public) ' fields
Dim mi() As MethodInfo = t.GetMethods(BindingFlags.Static BitOr _
BindingFlags.NonPublic BitOr BindingFlags.Public) ' methods
|
| C#
|
VB
|
|
下一步是迭代通过每个数组,并在屏幕上列出数组中的元素(显然,您将实际处理这些元素或标识数组中的某个特定元素)。有多种方法可以进行该操作,但在该示例中使用 Foreach(Visual Basic 中为 For Each)语句。
// iterate through all the method members
foreach (MethodInfo m in mi) {
Console.WriteLine(m);
}
// iterate through all the field members
foreach (FieldInfo f in fi) {
Console.WriteLine(f);
}
// etc.... for each array type
Dim m As MethodInfo
Dim f As FieldInfo
' iterate through all the method members...
For Each m In mi
Console.WriteLine(m)
Next m
' iterate through all the field members
For Each f In fi
Console.WriteLine(f)
Next f
' etc.... for each array type
|
| C#
|
VB
|
|
前面的代码工作良好,但请注意两条 Foreach(Visual Basic 中为 For Each)语句多么类似。将所有这些都写出来非常费力而且杂乱(并且如果以后更改代码,可能需要大量维护)。可以通过返回到 MemberInfo 对象来规避这一点。MemberInfo 对象包括所有可能的信息集(方法、字段和接口等等)。这可以对我们有所帮助,因为我们可以将以前的多个 Foreach 语句写成一个语句,传入数组以进行分析。
// call the routine below, passing the relevant array we made in the previous step
PrintMembers( mi ); // the method information
PrintMembers( fi ); // the field information
void PrintMembers (MemberInfo [] ms ) {
// MemberInfo is the generic info object. This can be any of the other info objects.
foreach (MemberInfo m in ms) {
Console.WriteLine(m);
}
}
' call the routine below, passing the relevant array we made in the previous step
PrintMembers( mi ) ' the method information
PrintMembers( fi ) ' the field information
Sub PrintMembers (ms() As MemberInfo)
Dim m As MemberInfo
' MemberInfo is the generic info object. This can be any of the other info objects.
For Each m in ms
Console.WriteLine(m)
Next m
End Sub
|
| C#
|
VB
|
|
运行该示例时您将注意到,同时会发生其他一些事情。不必硬编码 System.String 对象,可以指定要获取有关哪个类的信息。它还使您可以控制是要显示静态信息还是实例信息。
如何获取程序集内的类型本示例阐释如何检索给定程序集的所有类型。若要浏览程序集的类型,首先需要标识想操作的程序集。在使某对象引用了感兴趣的程序集后,可以在该程序集上调用 GetTypes 方法,它返回包含该程序集内所有类型的一个数组。您可以使用控制逻辑标识该数组中的更具体类型,并使用迭代逻辑分析您的数组,在需要时向用户返回类型信息。检索类型信息的功能对确定可用于给定任务的其他类型很有用,或对标识可为您提供所需功能的现有元素很有用。
从特定程序集检索类型时要学习的首要内容是如何标识程序集。本"快速入门"展示检索程序集的两种方法。第一种方法是标识要在程序集内查找的特定对象,并向程序集请求该对象的模块(记住模块是类型和代码的逻辑分组,如 .dll 或 .exe)。第二种方法是使用 Assembly 类的 LoadFrom 方法,为指定模块(如 myapp.exe)加载特定程序集。
// don't forget your using statements
using System;
using System.Reflection;
// ...
// Getting an Assembly, method 1. Get the mscorlib assembly
// Note that other types such as String, or Int32 would have worked just as well,
// since they reside in the same assembly
Assembly a = typeof(Object).Module.Assembly;
// Getting an Assembly, method 2. Load a particular assembly, using a reference to a
// module that is within that assembly. Note that this requires a compiled module for
// the reference, and when running in an aspx page, will require a fully qualifed path
// to the file, to ensure it is correctly identified
Assembly b = Assembly.LoadFrom ("GetTypes.exe");
// note that either of the above methods is viable, depending on the information
// you have. Since we know the name of the file which houses all of the base system
// objects, we could do the following to replace the first example, just as effectively
// (the absolute path may change on your machine)
// Assembly a = Assembly.LoadFrom
// ("c:/winserv/microsoft.net/framework/v1.0.2230/mscorlib.dll");
' don't forget your using statements
Imports System
Imports System.Reflection
' ...
' Getting an Assembly, method 1. Get the mscorlib assembly
' Note that other types such as String, or Int32 would have worked just as well,
' since they reside in the same assembly
Dim a As reflection.Assembly = GetType(Object).Module.Assembly
' Getting an Assembly, method 2. Load a particular assembly, using a reference to a
' module that is within that assembly. Note that this requires a compiled module for
' the reference, and when running in an aspx page, will require a fully qualifed path
' to the file, to ensure it is correctly identified
Dim b As reflection.Assembly = reflection.Assembly.LoadFrom ("GetTypes.exe")
' note that either of the above methods is viable, depending on the information
' you have. Since we know the name of the file which houses all of the base system
' objects, we could do the following to replace the first example, just as effectively
' (the absolute path may change on your machine)
' Dim a As reflection.Assembly = reflection.Assembly.LoadFrom _
' ("c:/winserv/microsoft.net/framework/v1.0.2230/mscorlib.dll")
|
| C#
|
VB
|
|
标识了程序集后,现在可以继续检索类型,将 GetTypes 方法的返回值分配给 Type 对象的数组。现在便可以操作这些类型了。在下面的示例中,您将获取核心运行时库的类型,并分别计算该程序集内不同类型样式的数目(如果需要有关 Foreach (For Each) 语句的更多信息,请参阅如何迭代通过集合主题下的内容)。尽管还可以计算其他成员(如类和枚举)的数目,但在该示例中,将只展示如何计算接口数目。
//Get all the types in the assembly identified in the previous example
Type [] types = a.GetTypes ();
int numInterfaces = 0;
foreach (Type t in types) {
//the following line uses a set of methods which identify what
//kind of type we are currently querying
if (t.IsInterface) {
// only print out the names of the Interfaces
Console.WriteLine (t.Name + "");
numInterfaces++;
}
}
// write out the totals
Console.WriteLine("Out of {0} types in the {1} library:",
types.Length, typeof(Object).Module.ToString());
Console.WriteLine ("{0} are interfaces (listed)", types.Length, numInterfaces);
' Get all the types in the assembly identified in the previous example
Dim types() As Type = a.GetTypes ()
Dim numInterfaces As Integer = 0
Dim t As Type
For Each t in types
' the following line uses a set of methods which identify what
' kind of type we are currently querying
If t.IsInterface Then
' only print out the names of the Interfaces
Console.WriteLine (t.Name + "'")
numInterfaces = numInterfaces + 1
End If
Next t
' write out the totals
Console.WriteLine("Out of {0} types in the {1} library:", _
types.Length, GetType(Object).Module.ToString())
Console.WriteLine ("{0} are interfaces (listed)", types.Length, numInterfaces)
|
| C#
|
VB
|
|
还可以使用第一个示例中标识的第二种方法检索给定程序集的类型。在下面的示例中,您将注意到它并未使用相同的基结构来依次通过类型,因为您不需要跟踪类型的不同种类。当查看小型程序集时这很适合,如当前运行的应用程序(获取 MSCorLib 的所有类型的列表将得到一个非常大的列表)。
// Get all the types in the assembly identified in the previous example (this assembly)
Type [] types2 = b.GetTypes ();
Console.WriteLine ("Get all the types from the assembly: '{0}'", b.GetName());
foreach (Type t in types2)
{
Console.WriteLine (t.FullName);
}
' Get all the types in the assembly identified in the previous example (this assembly)
Dim types2() As Type = b.GetTypes ()
Console.WriteLine ("Get all the types from the assembly: '{0}'", b.GetName())
For Each t in types2
Console.WriteLine(t.FullName) ' not many types, so we can print them all
Next t
|
| C#
|
VB
|
|
2005年3月2日
这个控件不错, 可以很方便地实现如vs 2003里那种窗口定位, 浮动, dock, tab化等界面效果, 今天有空下载来看了一下, 用起来还是很容易的, 大概讲一下, 省得以后忘记了.
dockpanel中提供了几个可用的类, 重要的有两个, 一是DockPanel, 一是DockContent,
DockPanel是从panel继承出来的, 用于提供可浮动的dock的子窗口进行浮动和dock的场所,
DockContent是从form类中继承出来的, 用于提供可浮动的窗口基类. 就是说: DockContent对象可以在DockPanel对象中任意贴边, 浮动, TAB化等.
建立一个docpanel风格的程序的过程是:
1. 建立一个winform程序, 引用WinFormsUI.dll;
2. 程序主窗口比如说是FrmMain; 把FrmMain.IsMdiContainer 设置为true;
3. 在FrmMain中放一个DockPanel, 比如说名称是Panel1, 把Panel1.Dock设为Fill, 或是者是你规划的地方.
4. 新建你的子文档窗口类, 就是新建一个windows FORM, 在窗口设计器里面随便你添上什么愿意要的控件. 这将是你的程序里的子窗口, 我弄了两个:
一个叫FrmChild, 里面放了一个dock fill的editbox,
另一个叫FrmProperty, 放了一个dock fill的PropertyGrid, 名叫propertyGrid1,
设这两个是有用的. 后面说.
5. 打开FrmChild和FrmProperty的代码, 把class的继承类由Form改为DockContent;
如:
public class FrmChild : WeifenLuo.WinFormsUI.DockContent // 注意这里改了!
  {

}
6. 为FrmMain加上两个私有成员:
FrmChild fc;
FrmProperty fp;
这两个成员用来保存待会儿新建的两个窗口的指针.
7. 为FrmMain的Load写些代码, 如下:
fc = new FrmChild();
fp = new FrmProperty();
fc.Show(this.dockPanel1);
fp.Show(this.dockPanel1);
fp.propertyGrid1.SelectedObject = fc;
fp.propertyGrid1.PropertyValueChanged +=new PropertyValueChangedEventHandler(propertyGrid1_PropertyValueChanged);
fc.MouseDown +=new MouseEventHandler(fc_MouseDown);
fc.MouseUp +=new MouseEventHandler(fc_MouseUp);
就是建立两个窗口, 这两个窗口都是从DockContent中继承的, 都有贴边, tab化, 自动hide的能力, 很有意思的.
注意DockContent有新的Show()方法, 可以指定要Show的DockPanel, 当然要写上你准备的那个dockPanel1;
两个都Show了, 然后为fp的propertyGrid1指定要显示的对象, 就是fc;
之后再注册事件处理程序, 这几句是为了可以实现属性变了窗口变, 窗口变了属性变的效果, 可以动态地看到这些对象的属性对于其外观和形为的影响, 对于只想建个多窗口程序的人来说, Show完了就可了.
8. 由于上面注册的事件, 下面加几个事件处理程序:
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
 {
this.dockPanel1.Invalidate();
this.fc.Invalidate();
//this.fc.Refresh();
}
private void fc_MouseDown(object sender, MouseEventArgs e)
 {
this.fp.propertyGrid1.Refresh();
}
private void fc_MouseUp(object sender, MouseEventArgs e)
 {
this.fp.propertyGrid1.Refresh();
}
运行吧, 看看效果? 你把这两个窗口放到边上, 这里哪里都试试, 挺有意思的.
你也可以借这个程序试试DockContent的各个属性的效果.
差点忘记, 这里有这东西的下载地址, 有源码的, 很不错! 就是从我这里连速度太慢了.
http://sourceforge.net/projects/dockpanelsuite/
|