新鲜、有趣,互联生活。令狐葱。

2006/12/31

C#中using的用法

上一篇文章中有用到using,上网查了一下,索性把using的用法列出来。
 
1. using指令。using + 命名空间名字,这个应该算是最常见的了,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间。
例:using System;
 
2. using别名。using + 别名 = 包括详细命名空间信息的具体的类型。 这个不经常使用,比如当同一个cs引用了两个不同的命名空间,但两个命名空间都包括了一个相同名字的类型的时候,这种特性就显得很好用了:〉
例:using aClass = NameSpace1.MyClass;
   
using bClass = NameSpace2.MyClass;
 
3. using语句,定义一个范围,在范围结束时处理对象。

当在某个代码段中使用了类的实例,而希望无论因为什么原因,只要离开了这个代码段就自动调用这个类实例的Dispose。当然,用try...catch来捕捉异常也可以实现,但是不如using方便。
例:
using  (Class1 cls1 = new Class1(), cls2  = new Class1())
{
  
//  the code using cls1, cls2


}
 // call the Dispose on cls1 and cls2

WinForm程序启动时不显示主窗体的方法(C#)

看了两篇文章(12),然后把文中的代码整理了以下,文中所述WinForm程序启动时不显示主窗体的实现方法主要有以下5种,个人觉得第五种最简单,而且效果也不错,第四种方法也值得推荐。
 
实现代码及简短解释如下:
//隐藏窗体的方法1/5:不指定任何窗体为主窗体
//注意:通常,在一个程序中,关闭主窗体,就可以关闭应用程序。
//但是在没有主窗体中,不行。
//只能使用Application.Exit()才能关闭应用程序。
//using可以保证Application结束前,关闭MyMainForm
using (new Form1())
{
    Application.Run();
};
//Application.Run(new Form1());

//隐藏窗体的方法2/5:
//通过close()关闭主窗口同时可以关闭应用程序
protected override CreateParams CreateParams
{
    get
    {
        Hide();
        return base.CreateParams;
    }
}

//隐藏窗体的方法3/5:
//这种方法仍然不能用Close主窗口的方式来关闭应用程序, 还得使用Application.Exit。
protected override void SetVisibleCore( bool value)
{
    base.SetVisibleCore(false);
}

//注意:方法2和3 使用Show好像没办法调出主窗口,比较郁闷。

//隐藏窗体的方法4/5 part1/2: 推荐使用!!!
//ApplicationContext实质上就是一个Application与主窗体之间的连接器,
//掌管着二者之间的互动关系。其中最主要的,就是负责在主窗体
//关闭时结束线程。既然如此,我们只要根据需要自定义一个ApplicationContext就可以了
internal class HideOnStartupApplicationContext : ApplicationContext
{
    private Form mainFormInternal;

   
// 构造函数,主窗体被存储在mainFormInternal
    public HideOnStartupApplicationContext( Form mainForm)
    {
        this.mainFormInternal = mainForm;

        this.mainFormInternal .Closed += new EventHandler(mainFormInternal_Closed);
    }

   
// 当主窗体被关闭时,退出应用程序
    void mainFormInternal_Closed(object sender, EventArgs e )
    {
        Application.Exit();
    }
}
//在Main中作如下修改:
HideOnStartupApplicationContext context = new HideOnStartupApplicationContext( new Form1());
Application.Run(context );

//隐藏窗体的方法5/5: 推荐使用!!!
//在构造函数中或者直接设置form属性
this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized;
 
程序中配合notifyIcon 控件一起使用,效果很好!
以上代码在VS2005下编译通过。

使应用程序只运行单个实例(C#)

代码如下:(在类中)
public partial class AppSingleton : Form
    {
        public AppSingleton()
        {
            InitializeComponent();
        }
         static Mutex m_Mutex;

        public static void Run()
        {
            if (IsFirstInstance()) {
                 Application.ApplicationExit += new EventHandler(OnExit);
                Application .Run();
            }

         }
        public static void Run (ApplicationContext context)
        {
            if (IsFirstInstance()) {
                Application.ApplicationExit += new EventHandler(OnExit);
                Application.Run(context );
            }
        }
        public static void Run(Form mainForm)
        {
             if (IsFirstInstance()) {

                Application.ApplicationExit += new EventHandler(OnExit);

                Application.Run(mainForm);


            } else {

                MessageBox.Show("应用程序已启动,请在任务管理中关闭后再启动!" , "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
         static bool IsFirstInstance()
        {
            m_Mutex = new Mutex(false, "SingletonApp Mutext" );
            bool owned = false;
            owned = m_Mutex .WaitOne(TimeSpan.Zero, false );
            return owned;
        }
        static void OnExit(object sender, EventArgs args )
        {
            m_Mutex.ReleaseMutex ();
            m_Mutex.Close();
         }
    }

主程序中(main)需要做如下修改:
AppSingleton.Run(new AppSingleton());
//Application.Run(new AppSingleton());

2006/12/30

C#中使用BackgroundWorker 实现后台操作窗体

编程的时候常常遇到需要处理很长时间的函数等,这时可能会导致用户界面 (UI) 似乎处于停止响应状态。在VS2005里可以使用 BackgroundWorker 类来杜绝这种UI假死的现象。

DoWork 事件:处理程序中调用耗时的操作。
RunWorkerAsync 事件:若要启动该操作。
ProgressChanged 事件:收到进度更新通知。
RunWorkerCompleted 事件在操作完成时收到通知。

下面通过一个程序说明,程序来自msdn(Link),有所删减。
 
public partial class FormFibonacci : Form
    {
        private int numberToCompute = 0;
         private int highestPercentageReached = 0 ;

        public FormFibonacci ()
        {
            InitializeComponent ();
         }

        
// 占时计算函数
        long ComputeFibonacci (int n, BackgroundWorker worker , DoWorkEventArgs e)
        {
            
// The parameter n must be >= 0 and <= 91.
             // Fib(n), with n > 91, overflows a long.
             if ((n < 0) || (n > 91 )) {
                 throw new ArgumentException (
                    "value must be >= 0 and <= 91", "n");
            }

             long result = 0;

            
// Abort the operation if the user has canceled.
             // Note that a call to CancelAsync may have set
             // CancellationPending to true just after the
             // last invocation of this method exits, so this
             // code will not have the opportunity to set the
             // DoWorkEventArgs.Cancel flag to true. This means
             // that RunWorkerCompletedEventArgs.Cancelled will
             // not be set to true in your RunWorkerCompleted
            // event handler. This is a race condition.
            if (worker .CancellationPending ) {
                e.Cancel = true;
            }
else {
                if (n < 2) {
                    result = 1;
                 } else {
                    result = ComputeFibonacci(n - 1, worker, e) +
                              ComputeFibonacci(n - 2, worker, e);
                }

                
// 报告进度
                 int percentComplete =
                    ( int)((float) n / ( float )numberToCompute * 100 );
                if (percentComplete > highestPercentageReached) {
                     highestPercentageReached = percentComplete;
                     worker.ReportProgress (percentComplete);
                }
             }

            return result;
         }

         private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
        {
            
// Get the BackgroundWorker that raised this event.
             BackgroundWorker worker = sender as BackgroundWorker;

            
// Assign the result of the computation
             // to the Result property of the DoWorkEventArgs
            // object. This is will be available to the
            // RunWorkerCompleted eventhandler.
            e.Result = ComputeFibonacci ((int)e.Argument , worker, e );
         }

        private void btnStart_Click( object sender, EventArgs e)
        {
            numberToCompute = (int)numericUpDown1 .Value ;
            highestPercentageReached = 0;
            
//Start the asynchronous operation.
             //注意:参数从这里传过去
            backgroundWorker1.RunWorkerAsync (numberToCompute);
         }

         private void btnCancel_Click (object sender, EventArgs e)
        {
            
// Cancel the asynchronous operation.
            this.backgroundWorker1 .CancelAsync();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            
// First, handle the case where an exception was thrown.
             if (e. Error != null) {
                MessageBox.Show(e.Error. Message);
             } else if (e .Cancelled) {
                
// Next, handle the case where the user canceled
                 // the operation.
                 // Note that due to a race condition in
                 // the DoWork event handler, the Cancelled
                 // flag may not have been set, even though
                 // CancelAsync was called.
                resultLabel .Text = "Canceled" ;
            } else {
                
// Finally, handle the case where the operation
                // succeeded.
                resultLabel.Text = e.Result. ToString();
             }

        }

         private void backgroundWorker1_ProgressChanged (object sender, ProgressChangedEventArgs e)
        {
            
//设置进度条
             progressBar1.Value = e.ProgressPercentage ;
        }
    }

 
 
在程序中需要注意以下几点:
 
1. 必须确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。
 
2. 如果后台操作需要参数,要在调用 RunWorkerAsync 时给出参数。在 DoWork 事件处理程序内部,可以从 DoWorkEventArgs.Argument 属性中提取该参数。
 
3. 报告进度函数 ReportProgress有一个参数(或者两个,参见msdn):
percentProgress 已完成的后台操作所占的百分比,范围从 0% 到 100%。
 
4. 注意取消操作和进度条的设置。
 
5. 可以使用 IsBusy 属性可以确定 BackgroundWorker 线程是否仍在运行。如果代码在主 UI 线程上(对于 Click 事件处理程序即是如此),请务必调用 System.Windows.Forms.Application.DoEvents 方法以使用户界面能够响应用户操作。在btnClick中可以有:
while (this.backgroundWorker1.IsBusy)
    {
        Application.DoEvents();
    }
 
6. 在试图访问 System.ComponentModel.RunWorkerCompletedEventArgs.Result 属性或可能已受 DoWork 事件处理程序影响的任何其他对象之前,一定要在 RunWorkerCompleted 事件处理程序中检查 System.ComponentModel.AsyncCompletedEventArgs.Error 属性。

C#中的事件和委托(原)

事件在VS中使用起来很方便,拖一个按钮上去,然后双击,就会看到OnClick(...)的响应函数。但是真要深究,还真有点学问。今天看了MSDN上的几个关于事件和委托的介绍,感觉��里��唆,废了半天劲才看了个半懂,暂时记下。
 
相关知识点
 
1. 事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。引发事件的对象称为事件发送方。捕获事件并对其作出响应的对象叫做事件接收方。

2. 在事件通信中,事件发送方类不知道哪个对象或方法将接收到(处理)它引发的事件。所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。.NET Framework 定义了一个特殊的类型(Delegate),该类型提供函数指针的功能。

3. 委托是可保存对方法的引用的类。与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。这样,委托就等效于一个类型安全函数指针或一个回调。虽然委托具有许多其他的用途,但这里只讨论委托的事件处理功能。一个委托声明足以定义一个委托类。声明提供委托的签名,公共语言运行库提供实现。

4. 只有当事件生成事件数据时才需要自定义事件委托。许多事件,包括一些用户界面事件(例如鼠标单击)在内,都不生成事件数据。在这种情况下,类库中为无数据事件提供的事件委托 System.EventHandler 便足够了。
delegate void EventHandler(object sender, EventArgs e);

5. 事件功能是由三个互相联系的元素提供的:提供事件数据的类、事件委托和引发事件的类。.NET Framework 具有命名与事件相关的类和方法的约定。如果希望您的类引发一个名为 EventName 的事件,您需要以下元素:

包含事件数据的类,名为 EventNameEventArgs。该类必须从 System.EventArgs 导出。

事件的委托,名为 EventNameEventHandler。

引发事件的类。该类必须提供事件声明 (EventName) 和引发事件 (OnEventName) 的方法。

6. .NET Framework 类库或第三方类库中可能已经定义了事件数据类和事件委托类。在这种情况下,您就不需要定义这些类了。例如,如果您的事件不使用自定义数据,可以使用 System.EventArgs 作为事件数据并使用 System.EventHandler 作为委托。

7. 若要订阅事件,接收器必须创建一个与事件具有相同类型的委托,并使用事件处理程序作为委托目标。然后,接收器必须使用加法赋值运算符 (+=) 将该委托添加到源对象的事件中。
clock.Alarm += new AlarmEventHandler(w.AlarmRang);
若要取消订阅事件,接收器可以使用减法赋值运算符 (-=) 从源对象的事件中移除事件处理程序的委托。
 
源代码
 
第一个例子使用系统自己的System.EventArgs 作为事件数据,如下所示(这就相当于直接拖控件,双击实现的那个:〉)其中,Clock 类定义公共事件 Alarm,并提供引发该事件的方法。WakeMeUp 类定义处理 Alarm 事件的 AlarmRang 方法。Program类一起使用类,将使用 WakeMeUp 的 AlarmRang 方法设置为处理 Clock 的 Alarm 事件。
using System;

namespace testEvent
{
    public delegate void AlarmEventHandler(object sender, EventArgs e);

    public class Clock
    {
        public event AlarmEventHandler Alarm;

        
// The protected OnAlarm method raises the event by invoking
        
// the delegates. The sender is always this, the current instance
        
// of the class.
        
//
         protected virtual void OnAlarm(EventArgs e)
        {
            if (Alarm != null) {
                
// Invokes the delegates.
                Alarm( this, e);
             }
        }

        internal void Start()
        {
            OnAlarm(EventArgs.Empty );
        }
    }

    public class WakeMeUp
    {
        public void AlarmRang( object sender, EventArgs e) {
            System. Console.WriteLine("Event has been invoked!");
        }
    }

     class Program
    {
         static void Main(string[] args)
        {
            
// Instantiates the event receiver.
            WakeMeUp w = new WakeMeUp ();

            
// Instantiates the event source.
            Clock clock = new Clock();

            
// Wires the AlarmRang method to the Alarm event.
            clock.Alarm += new AlarmEventHandler(w.AlarmRang);

            clock.Start();
        }
    }
}
 
我们再把上面的例子扩展一下,实现一个简单的AlarmEventArgs 类定义 Alarm 事件特定的数据。
namespace testEvent
{
    public class AlarmEventArgs : EventArgs
     {
        public string AlarmText;

         public AlarmEventArgs(string alarmText)
        {
             AlarmText = alarmText;
        }
    }

     public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

    public class Clock
    {
        public event AlarmEventHandler Alarm;
        public string state = "the clock is ringing...";

         protected virtual void OnAlarm(AlarmEventArgs e)
        {
             if (Alarm != null) {
                
// Invokes the delegates.
                 Alarm(this, e);
            }
        }

         internal void Start()
        {
            System .Threading.Thread.Sleep(300);
            AlarmEventArgs e = new AlarmEventArgs("Please wake up!" );
            OnAlarm(e);
  
        
}
    }

    public class WakeMeUp
    {
        public void AlarmRang( object sender, AlarmEventArgs e)
        {
            Console .WriteLine(e.AlarmText);
            Console.WriteLine ("The state of clock:");
            Console. WriteLine(((Clock)sender).state);

            Console .ReadLine();
        }
    }

    class Program
    {
        ......
    }
}


参考和延伸阅读(需有VS2005 MSDN)
 

2006/12/29

C#实现.net组件和COM组件的互操作

.net 框架自微软推出以来受到了很高的评价,但是无论如何,以前的东西诸如COM还是没办法完全丢弃的。本文试图通过两个小的例子,来测试一下.net组件和COM组件是如何相互调用和操作的,由于例子比较简单,估计会遗漏不少知识点。
 
COM组件中调用.net编写的组件
 
首先使用C#写一个组件(其实就是实现一个DLL),代码如下(保存为hello.cs):
using System;
using System.Collections.Generic;
using System.Text;

namespace Hello
{
    public class Hello
    {
        public void sayHello()
        {
             System.Console.WriteLine("Hello World!");
        }
    }
}
 
在命令行中执行
C:\>csc /t:library /out:Hello.dll hello.cs
即可生成一个dll库文件 Hello.dll。但是想要在现有的COM组件中使用这个组件,还需要进行如下操作:
C:\>regasm hello.dll /regfile hello.reg
上面的命令行是注册我们的.NET组件,并且产生了一个备用的注册表文件。其实就相当于在win9x/NT/2000下面注册COM组件使用的命令regsvr32 c:\test.dll 。在.NET下面,注册.NET组件的命令是regasm,注意,这个方法仅仅是为了可供COM组件来调用,.NET本身之间相互调用组件是不需要任何注册。
下面需要产生一个.NET组件的类型库,目的是为了我们在COM组件中进行提前绑定操作:
C:\>tlbexp hello.dll /out:hello.tlb
 
接下来就可以在自己的基于目前COM技术的代码中使用上面我们使用C#编写的.NET组件了。在这里,我们使用VB6.0编写一个小小的测试代码,在开始之前我们需要在VB的集成环境中使用菜单中的"引用"选项,选择我们刚才产生的类型库文件Hello.tlb。

VB代码如下:
Private Sub Form_Load()
Dim test As New Hello.hello
Dim str As String

str = test.sayHello()
MsgBox str
End Sub
 
.NET组件中使用目前存在的COM组件
 
对于.NET来讲,使用COM组件就要简单一些。..NET提供了大量的类库来方便的实现同COM的相互操作,其中很重要的一个名称空间就是:System.Runtime.InteropServices。通过这个名称空间的名字我们也可以从字面上看出,"互操作服务"。System.Runtime.InteropServices这个名称空间提供了一系列的类来对COM对象进行操作。

下面的例子中,我们来调用一下系统自带的Win32函数MessageBoxA, 这个函数位于系统的COM组件user32.dll当中,我们调用的代码如下:

using System;
using System.Runtime.InteropServices;

class Test
{
    [DllImport ("user32.dll")]
    public static extern int MessageBoxA( int hWnd,string strMsg,string strCaption, int nType);

    public static void Main()
    {
        int myMsg;
        myMsg =MessageBoxA(0,"Hello!" ,"test",0);
    }
}
 
切换到命令行,运行:
C:\>csc Test.cs
编译完毕我们的C#应用程序之后,直接运行就可以看到对话框了!同样的,也可以使用这种方法来调用我们自己使用VB/VC编写的COM组件。

需要注意的是,在调用COM组件之前,我们需要在.NET程序中引用名称空间:System.Runtime.InteropServices 。因为我们需要使用这个名称空间所提供的一个方法:DllImport。

验证18位身份证号码算法(C#)

说明
 
今天偶然间在网上看到关于18位身份证各位的含义以及计算方法,于是上网找到了一个验证18位身份证号码的C#程序片断,经测试,实现的效果很好。自己用手计算了一下,这才发现,原来第18位是这样计算出来的,这样的话似乎这一位就是多余的了啊,不是么?
 
背景知识
 
18位身份证标准在国家质量技术监督局于1999年7月1日实施的GB11643-1999《公民身份号码》中做了明确的规定。 GB11643-1999《公民身份号码》为GB11643-1989《社会保障号码》的修订版,其中指出将原标准名称"社会保障号码"更名为"公民身份号码",另外GB11643-1999《公民身份号码》从实施之日起代替GB11643-1989。GB11643-1999《公民身份号码》主要内容如下:
一、范围
     该标准规定了公民身份号码的编码对象、号码的结构和表现形式,使每个编码对象获得一个唯一的、不变的法定号码。
二、编码对象
     公民身份号码的编码对象是具有中华人民共和国国籍的公民。
三、号码的结构和表示形式
1、号码的结构
    公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
2、地址码
    表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。
3、出生日期码
    表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
4、顺序码
     表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
5、校验码
(1)十七位数字本体码加权求和公式
S = Sum(Ai * Wi), i = 0, ... , 16 ,先对前17位数字的权求和
Ai:表示第i位置上的身份证号码数字值
Wi:表示第i位置上的加权因子
Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
(2)计算模
Y = mod(S, 11)
(3)通过模得到对应的校验码
Y: 0 1 2 3 4 5 6 7 8 9 10
校验码: 1 0 X 9 8 7 6 5 4 3 2
四、举例如下:
北京市朝阳区: 11010519491231002X
广东省汕头市: 440524188001010014
 
源代码(C#实现)
private string CheckCidInfo(string cid)
        {
        string [] aCity = new string[] {null, null, null, null, null,

            
null, null, null, null, null , null, "北京", "天津",

            
"河北", "山西", "内蒙古" , null, null, null, null, null ,

            
"辽宁" , "吉林", "黑龙江", null , null, null, null, null,

            
null, null, "上海", "江苏", "浙江", "安徽", "福建" ,
            "江西", "山东", null , null, null, "河南", "湖北", "湖南" ,
            "广东", "广西", "海南" ,null,null, null,"重庆","四川" ,
            "贵州","云南" ,"西藏",null, null,null,null ,null,null,"陕西" ,
            "甘肃", "青海","宁夏","新疆" ,null,null,null ,null,null,
            "台湾",null, null,null,null, null,null,null ,null,null,"香港" ,
            "澳门", null,null,null, null,null,null ,null,null,"国外" };
        double iSum =0;
        
//string info="";
        System.Text.RegularExpressions .Regex rg = new System.Text.RegularExpressions.Regex( @"^\d{17}(\d|x)$");
        System .Text.RegularExpressions.Match mc = rg.Match(cid);
         if(!mc.Success)
        {
            return "" ;
        }

        
cid = cid.ToLower();
        cid = cid .Replace("x","a");
        if(aCity[int .Parse(cid.Substring(0, 2))] == null) {
             return "非法地区";
         }
        try {
            DateTime.Parse(cid.Substring( 6,4) + "-" + cid.Substring(10,2 ) + "-" + cid.Substring(12 ,2));
        } catch {
             return "非法生日";
        }
        for (int i = 17; i >= 0; i--) {

            
iSum +=(System.Math.Pow(2 ,i) % 11 ) * int .Parse(cid[17-i].ToString(),System .Globalization.NumberStyles.HexNumber);
        }
        if(iSum % 11 != 1)
            return ("非法证号");

        return (aCity[int.Parse(cid.Substring (0,2))] + "," + cid.Substring(6, 4) + "-" + cid.Substring( 10,2) + "-" + cid.Substring(12,2 ) + "," + (int. Parse(cid.Substring(16,1 )) % 2 == 1 ? "男" : "女"));

         }