代码改变世界

.NET开发常见问题两则

2007-09-22 01:01  Timothy Ye  阅读(202)  评论(0编辑  收藏  举报
一、关于DateTime
      在将DateTime类型,插入到数据库的时候,最容易出现的一种错误:
    “SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间”
    原因是我们在取DateTime.MinValue的值,并插入到数据库的时候,DateTime.MinValue值范围和数据库DateTime类型数据范围不一致造成的。数据库中,DateTime类型字段,最小值1/1/1753 12:00:00,而.NET Framework中,DateTime类型,最小值为1/1/0001 0:00:00,显然,超出了Sql的值的最小范围,导致数据溢出的错误。

    解决方法:使用System.Data.SqlTypes.SqlDateTime.MinValue替代System.DateTime类型,这样SqlDateTime的MinValue和Sql中DateTime的范围吻合,就不会再出现以上的错误了。

二、关于跨线程调用控件的问题
    在.NET 2.0中,我们常常需要在新建的线程,比如一个工作线程中访问UI控件,程序编译没有任何错误,但是在运行的时候,会抛出异常:InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”
    原因是因为:访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。

    我们看看有问题的代码:建立新线程,并试图在线程中跨线程调用UI控件
    

       private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(new ThreadStart(TestFunc));
            thread.IsBackground = true;
            thread.Start();
        }

        private void TestFunc()
        {
            Thread.Sleep(3000);
            label1.Text = "ok!";
        }

      


      这样在运行的时候,就会抛出InvalidOperationException异常。

    解决方法:
    方法1.关闭跨线程调用控件的检查,来屏蔽掉此异常
    需要将Control.CheckForIllegalCrossThreadCalls 设为 false即可。(不推荐)
    
    方法2.使用Control类的Invoke和BeginInvoke
      为什么使用Invoke和BeginInvoke可以解决问题呢?因为Control的Invoke和BeginInvoke的参数为delegate,委托的方法是在Control的线程上执行的,也就是我们平时所说的UI线程。而控件也是在UI线程,所以不存在跨线程调用控件的问题。于是我们有了改进的代码:

    

       private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(new ThreadStart(TestFunc));
            thread.IsBackground = true;
            thread.Start();
        }

        private void TestFunc()
        {
            Thread.Sleep(3000);
            this.Invoke(new SetLabel(SetLabelText), new string[] { "ok!" });
        }

        private delegate void SetLabel(string s);

        private void SetLabelText(string s)
        {
            label1.Text = s;
        }

      

    
    建立一个委托,调用Control类的Invoke方法,让UI线程去执行委托实例化的方法。这样就不会出现异常了。