| David's profileprowyh's spaceBlogLists | Help |
|
November 27 IEnumerable & IEnumeratorpublic interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
bool MoveNext();
void Reset();
Object Current { get; }
}
IEnumerable和IEnumerator有什么区别?这是一个很让人困惑的问题(在很多forum里都看到有人在问这个问题)。研究了半天,得到以下几点认识:
1、一个Collection要支持foreach方式的遍历,必须实现IEnumerable接口(亦即,必须以某种方式返回IEnumerator object)。
2、IEnumerator object具体实现了iterator(通过MoveNext(),Reset(),Current)。
3、从这两个接口的用词选择上,也可以看出其不同:IEnumerable是一个声明式的接口,声明实现该接口的class是“可枚举(enumerable)”的,但并没有说明如何实现枚举器(iterator);IEnumerator是一个实现式的接口,IEnumerator object就是一个iterator。
4、IEnumerable和IEnumerator通过IEnumerable的GetEnumerator()方法建立了连接,client可以通过IEnumerable的GetEnumerator()得到IEnumerator object,在这个意义上,将GetEnumerator()看作IEnumerator object的factory method也未尝不可。
Collection & Iterator in .NET在OOP术语中,Collection是指包含其它对象的一种数据结构。C++ STL泛称为Container,.NET Framework泛称为Collection。
Iterator是一种遍历Collection的机制:an iterator is an object which allows a programmer to traverse through all the elements of a collection, regardless of its specific implementation.
Collection提供了聚集对象的容器(Container),Iterator提供了访问容器中对象的统一方法,Collection与Iterator两者结合,为开发人员提供了表示、操纵各种复杂数据结构的强大工具。
例一:Collection与Iterator在C# 1.0中的实现
using System; 例二:Collection & Iterator在C# 2.0中的实现 using System; 从例二可以看出,C# 2.0通过iterator block大大简化了Collection的遍历机制(iterator)。实际上,iterator block是C# 2.0为程序员提供的一种便利设施(handy facility),C#编译器在编译时,仍然生成如例一所示的enumerator class。
November 26 Thread & Thread ApartmentStateApartment是COM的概念,在.NET中本不涉及Apartment,但如果需要与COM互操作(COM Interop),则需要适当设置.NET线程的ApartmentState。
1、Attribute设置只适用于入口点方法(entry method)如Main(),对其它方法无意义;
2、获取ApartmentState使用GetApartmentState(),设置ApartmentState使用SetApartmentState(ApartmentState.Enum)。
示例如下:
using System;
using System.Threading; namespace Levensoft
{ public class ThreadApartmentState { public static void Main() { Thread t = new Thread(new ThreadStart(ThreadTest)); Console.WriteLine("t.ApartmentState: " + t.GetApartmentState().ToString()); t.SetApartmentState(ApartmentState.STA);
Console.WriteLine("t.ApartmentState: " + t.GetApartmentState().ToString()); t.Start();
} public static void ThreadTest()
{ Thread tt = Thread.CurrentThread; Console.WriteLine("tt.ApartmentState: " + tt.GetApartmentState().ToString()); } } } 此例的输出: t.ApartmentState: Unkown
t.ApartmentState: STA
tt.ApartmentState: STA
通过此例归纳出的简单结论:
1、通过new Thread()创建的线程,初始ApartmentState为Unkown;
2、可以通过SetApartmentState()设置线程的ApartmentState为STA或MTA;
3、线程的ApartmentState只可以设置一次,而且是在调用Start()启动线程之前设置。
4、线程的ApartmentState只能在其创建者中设置,而不能在线程本身的代码中设置,因为线程代码的执行是由Start()启动的。
November 20 .NET Configuration.NET Configuration对于系统及应用程序的运行环境配置提供了极大的灵活性,但由于版本的关系,配置信息的表述和读取显得相当混乱,许多文章甚至书籍中给出的示例代码都不能在.NET 2.0下运行,下面给出一个可以运行的例子。
1、config文件片断
<configuration>
<appSettings>
<add key="logfile" value="C:\logfile\event.log" />
</appSettings>
<connectionStrings>
<add name="myConnString"
providerName="System.Data.SqlClient"
connectionString="Server=(local);Database=mydb;uid=sa;pwd=mypass" />
</connectionStrings>
</configuration>
此config文件片断定义了两项内容:一是logfile文件名,二是数据库连接字符串。
对于Web Forms应用,将此文件命名为web.config并存于网站的根目录下。
对于Windows Forms应用,将此文件命名为app.config并存于应用程序的根目录下。如程序名为wordpad.exe,则config文件为wordpad.exe.config,且与wordpad.exe位于同一目录下。
2、Web Forms应用中对配置信息的读取
下面的代码读入上述config文件中定义的内容:
// open config file
Configuration config = WebConfigurationManager.OpenWebConfiguration("/");
// get logfile
AppSettingsSection appSettings = config.AppSettings as AppSettingsSection;
string logfile = appSettings.Settings["logfile"].Value;
// get connectionString
ConnectionStringsSection cnstrSection = config.ConnectionStrings;
ConnectionStringSettingsCollection cnstrCollection = cnstrSection.ConnectionStrings;
ConnectionStringSettings cnstrSettings = cnstrCollection["myConnString"];
string connectionString = cnstrSettings.ConnectionString;
3、Windows Forms应用中对配置信息的读取
Windows Forms读取config文件的代码,除了打开文件的代码不同之外,其余与Web Forms中的读取代码是一样的。下面给出一种简化的读取代码:
// open config file
Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
// get logfile
AppSettingsSection appSettings = config.AppSettings as AppSettingsSection;
string logfile = appSettings.Settings["logfile"].Value;
// get connectionString
string connectionString = config.ConnectionStrings.ConnectionStrings["myConnString"].ConnectionString;
.NET Configuration极其灵活,这里给出的只是一个非常简单的例子,深入研究容待后续。 November 07 Simplified InstallUtil.exe下面的代码演示了InstallUtil.exe的实现原理:
//////////////////////////////////
// SimpleInstall.cs // Simplified InstallUtil.exe ////////////////////////////////// using System;
using System.ComponentModel; using System.Collections; using System.Configuration.Install; using System.IO; namespace SimpleInstallUtil
{ public class SimpleInstall { public static void Main(string[] args) { ArrayList options = new ArrayList(); string myOption; bool toUninstall = false; bool toPrintHelp = false; TransactedInstaller transactedInstaller = new TransactedInstaller(); AssemblyInstaller assemblyInstaller; InstallContext installContext; try
{ for (int i = 0; i < args.Length; i++) { // process the arguments if (args[i].StartsWith("/") || args[i].StartsWith("-")) { // options myOption = args[i].Substring(1); // which option ?
if (String.Compare(myOption, "u", true) == 0 || String.Compare(myOption, "uninstall", true) == 0) { toUninstall = true; continue; } if (String.Compare(myOption, "?", true) == 0 || String.Compare(myOption, "help", true) == 0) { toPrintHelp = true; continue; } // other options, save it
options.Add(myOption); } else { // assembly to be installed if (!File.Exists(args[i])) { Console.WriteLine("Error: {0} - Assembly file doesn't exist.", args[i]); return; } // create an instance of 'AssemblyInstaller' that installs the given assembly
assemblyInstaller = new AssemblyInstaller(args[i], (string[])options.ToArray(typeof(string))); // add the instance of 'AssemblyInstaller' to the 'TransactedInstaller'
transactedInstaller.Installers.Add(assemblyInstaller); } } // help ?
if (toPrintHelp || transactedInstaller.Installers.Count == 0) { PrintHelp(); return; } installContext = new InstallContext("SimpleInstall.log", (string[])options.ToArray(typeof(string)));
transactedInstaller.Context = installContext; // install Or uninstall ?
if (!toUninstall) { transactedInstaller.Install(new Hashtable()); } else { transactedInstaller.Uninstall(null); } } catch (Exception exp) { Console.WriteLine("Exception: {0}", exp.Message.ToString()); } } public static void PrintHelp()
{ Console.WriteLine("Usage: SimpleInstall [/u | /uninstall] [option [...]] assembly [[option [...]] assembly [...]]"); Console.WriteLine("SimpleInstall executes the installers in each of the given assembly."); Console.WriteLine("If /u or /uninstall option is given it uninstalls the assemblies."); } } } 由此可见,InstallUtil.exe的实现原理是比较简单的:
1、分析命令行参数;
2、构造AssemblyInstaller;
3、由AssemblyInstaller构造TransactedInstaller;
4、调用TransactedInstaller的Install或Uninstall方法完成安装或卸载。
由此代码可以体会OOP的美妙之处:AssemblyInstaller和TransactedInstaller构成了InstallUtil.exe的核心,InstallUtil.exe只需要构造the instance of TransactedInstaller,然后调用其方法即可。而TransactedInstaller实例由多个AssemblyInstaller实例构成,所以任务归结为构造AssemblyInstaller的实例。而AssemblyInstaller实例的构造通过解析命令行参数完成。 November 06 Installer Tools (installutil.exe).NET Framework提供了InstallUtil.exe安装工具用于某些应用程序的安装。
某些服务器应用程序除了传统的程序文件之外,还关联了一些资源,如消息队列(message queues)、事件日志(event logs)、性能计数器(performance counters)等,在部署应用程序时,必须创建这些相关资源,而在卸载应用程序时,也必须同时删除这些资源。.NET Framework提供了安装组件(installer components)用于完成这些任务。
Installer components是导出自System.Configuration.Install.Installer的用户自定义Class,带有[RunInstaller(true)] attribute:
[RunInstaller(true)]
public class MyInstaller
{
// .....
}
System.Configuration.Install.Installer类实现了四个方法:Install, Commit, Rollback, Uninstall,用于实现事务性的(transacted)安装/卸载功能。
InstallUtil.exe安装工具通过反射方法获取要安装的程序集的installer component,调用其中的Install进行安装,如果Install执行成功,则最后执行Commit提交安装;如果在执行Install的过程中出现错误,则执行Rollback完成回滚,使系统恢复到安装前的状态。
InstallUtil.exe /u 调用Uninstall方法完成卸载。注意:卸载是非事务性的,亦即如果卸载过程中出现错误,则忽略,继续卸载其余部分。
InstallUtil.exe安装完成后,在当前目录产生三个文件:
由此简单说明可以看出,.NET Framework通过InstallUtil.exe/Installer Components对服务器端应用程序提供了一个框架性的安装工具,方便了Windows Service这类服务器端应用程序的开发。 下面的例子演示了Installer Class的用法: using System; namespace InstallerDemo // Attach the 'AfterInstall' event // Attach the 'Committed' event // Attach the 'Committing' event // Event handler for 'BeforeInstall' // Event handler for 'AfterInstall' // Event handler for 'Committed' // Override the 'Install' method // Override the 'Commit' method // Override the 'Rollback' method public static void Main() 例子中注册事件处理的代码利用了C#的简化语法: this.BeforeInstall += MyInstallerBeforeInstall; 完整的语法应该是: this.BeforeInstall += new InstallEventHandler(MyInstallerBeforeInstall); 由此可见,C# syntactical sugar为开发人员(特别是熟悉C/C++的开发人员)提供了莫大的便利,使得代码更加自然与直观。 上述的InstallEventHandler的声明是: public delegate void InstallEventHandler(Object sender, InstallEventArgs args); 根据此delegate的声明,可知MyInstallerBeforeInstall的声明必须是: void MyInstallerBeforeInstall(Object sender, InstallEventArgs args); Windows Service with C#最近需要用C#写一个Windows Service(以前称为NT Service),Google了一下,发现很多相关文章都是依据Visual Studio .NET的,但因为VS .NET隐含了很多细节,对我们搞清Windows Service with C#的实现机制没有多大帮助。下面给出一个手写的Windows Service with C#的skeleton:
// file: cservice.cs
using System;
using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.ServiceProcess; using System.Configuration.Install; namespace Levensoft.Service.WindowsService
{ // Windows Service class public class MyFirstService : System.ServiceProcess.ServiceBase { public MyFirstService() { this.CanPauseAndContinue = true; this.CanShutdown = true; this.CanHandleSessionChangeEvent = false; this.ServiceName = "MyFirstService";
} protected override void OnStart(string[] args)
{ } protected override void OnStop()
{ } protected override void OnContinue()
{ } public static void Main()
{ ServiceBase[] servicesToRun; servicesToRun = new ServiceBase[] {new Levensoft.Service.WindowsService.MyFirstService()};
ServiceBase.Run(servicesToRun); } } // Windows Service Installer Components
[RunInstaller(true)] public class ProjectInstaller : System.Configuration.Install.Installer { private ServiceProcessInstaller myServiceProcessInstaller; private ServiceInstaller myServiceInstaller; public ProjectInstaller()
{ this.myServiceProcessInstaller = new ServiceProcessInstaller(); this.myServiceInstaller = new ServiceInstaller(); // myServiceProcessInstaller
// Account or Username & Password this.myServiceProcessInstaller.Account = ServiceAccount.LocalSystem; this.myServiceProcessInstaller.Username = null; this.myServiceProcessInstaller.Password = null; // myServiceInstaller
// ServiceName & StartType this.myServiceInstaller.ServiceName = "MyFirstService"; this.myServiceInstaller.StartType = ServiceStartMode.Automatic; // ProjectInstaller
this.Installers.AddRange(new Installer[] {this.myServiceProcessInstaller, this.myServiceInstaller}); } } } 1、编译
csc cservice.cs
2、安装
installutil cservice.exe
3、启动服务
net start MyFirstService
4、停止服务
net stop MyFirstService
5、卸载
installutil /u cservice.exe
|
|
|