编程疑难杂症の莫名的神秘联结

引言:在自己使用Microsoft Visual C# 速成版编程过程中,时不时总出现一些很郁闷的问题,一直尚未得到解决,在此特地列出来,向园里的朋友们求助讨论一番。

注:本人已经Google和百度求助过,但是没有找到满意的答案,当然不排除自己的搜商还不够,假如哪位朋友能帮找出来,那更好!

问题说明:

      最近项目“聊天记录管理器”已经趋于稳定了。绝大部分的功能已经定型并且可用了。[By:Asion Tang]

所以接下来的工作基本上就是对代码进行维护,并逐渐完善原有功能。就在这个过程中,就又出现了一个莫名不得其解的问题。详细描述如下:

这次要进行完善的功能是:【自动检测飞信号码】功能。为此已经完整的重构到如下函数中:

        /// <summary>
        /// 自动检测短信中是否有飞信聊天记录。
        /// </summary>
        private void YEAutoCheckFeiXin()
        {
            if ( ckbAutoCheckFeixin.Checked )
            {
                lstFeiXinBackup.Clear();

                //将当前状态备份为一个快照。即不想检测飞信的时候能够恢复原来的数据。
                lstFeiXinBackup.AddRange(lsvMessageShower.Items.Cast<ListViewItem>());
                Debug.Print(lstFeiXinBackup[ 1 ].SubItems[ 1 ].Text);//显示已经被赋值后的lstFeiXinBackup变量里面的第二个元素的值。

                foreach ( ListViewItem tmp in lsvMessageShower.Items )
                {
                    if ( tmp.SubItems[ 0 ].Text != "sms" )
                        continue;

                    if ( tmp.SubItems[ 1 ].Text.Contains("12520") )
                    {
                        tmp.SubItems[ 1 ].Text = tmp.SubItems[ 4 ].Text.Remove(tmp.SubItems[ 4 ].Text.IndexOf(':'));
                        tmp.SubItems[ 4 ].Text = tmp.SubItems[ 4 ].Text.Remove(0 , tmp.SubItems[ 4 ].Text.IndexOf(':') + 1);
                    }
                    else if ( tmp.SubItems[ 2 ].Text.Contains("12520") )
                        tmp.SubItems[ 2 ].Text = MyNickName.Text;//作为个人网名
                    else
                        continue;

                    //设置为飞信的时间格式。
                    tmp.SubItems[ 3 ].Text = tmp.SubItems[ 3 ].Text.Replace('.' , '-');
                    tmp.SubItems[ 3 ].Text += ":00";
                }

                Debug.Print(lstFeiXinBackup[ 1 ].SubItems[ 1 ].Text);//再次显示该元素的值,用于和第一个显示的值想比较。
            }
            else
            {
                if ( lstFeiXinBackup.Count == 0 )
                    return;
                lsvMessageShower.Items.Clear();
                lsvMessageShower.Items.AddRange(lstFeiXinBackup.ToArray());
                //马上初始化此快照。
                lstFeiXinBackup.Clear();
            }
        }

这个函数在“自动检测飞信”的复选框按钮的Checked属性更改的时候调用。当勾选此复选框之后,进入检测代码;首先保存一个当前列表框的状态,这里我使用的是泛型List<>来对其进行保存,由于ListView控件本身没有对应函数转换成List可以AddRange的格式,所以这里我使用Cast<Type>来满足这个需求。

代码很成功的把ListView控件中的所有数据保存到了泛型List变量里面了,第一个Debug正确打印出了一个值,是未经修改的手机号码如下格式:12520xxxxxxxxxx;这是飞信的号码段。于是在下面的Foreach循环中不断的检测每一行,把凡是此号码段的都修改为标准的飞信聊天记录格式:(XX网名,时间,聊天内容)。而这个操作呢就是对ListView控件中的发送人1手机号码进行修改为对应网名。

接下来,问题就来咯。在第二个Debug的打印中,发现之前打印的那个元素此刻的值居然也发生了改变。打印出的是在Foreach循环中改变后的值!

症状诊断:

打一出现这个问题,我就想到了一个词“引用类型”。难道问题的源头就是引用类型?!感觉翻看曾经的读书笔记:

[By:Asion Tang] 果然不出所料。借此机会强化了对泛型List<Type>的印象了。在此引用微软帮助如下:

 

 

性能注意事项:
在决定使用 List<(Of <(T>)>) 还是使用 ArrayList 类(两者具有类似的功能)时,记住 List<(Of <(T>)>) 类在大多数情况下执行得更好并且是类型安全的。如果对 List<(Of <(T>)>) 类的类型 T 使用引用类型,则两个类的行为是完全相同的。但是,如果对类型 T 使用值类型,则需要考虑实现和装箱问题。

 

由此可以推知我使用的ListViewItem类型是引用类型。赶紧查看一下帮助。

暂时没有找到解决方案!

[By:Asion Tang]2010年12月17日 20:43:07

解决方案:

      今天一早起床(呵呵,其实是中午十二点才起的),当自己再次面对昨天的这个问题,思考怎么对付的时候,有了一个不错的想法:既然是在引用类型出了错,那么要解决的问题就是该怎么样实现深度复制(Deep Copy)的功能,负责把ListView控件里面的每一项数据完整而独立的生成一个备份!

      记得昨天在查询ListViewItem类型的MSDN帮助的时候,发现了在它的类声明里,实现了ICloneable借口!意思就是说

[By:Asion Tang] 列表框的[数据项]本身其实提供了一个Clone方法,貌似可以实现深度复制(Deep Copy)功能。于是赶紧编码测试,修改后代码如下:

        /// <summary>
        /// 自动检测短信中是否有飞信聊天记录。
        /// </summary>
        private void YEAutoCheckFeiXin()
        {
            if ( ckbAutoCheckFeixin.Checked )
            {
                lstFeiXinBackup.Clear();
                foreach ( ListViewItem tmp in lsvMessageShower.Items )
                {
                    /* 将当前状态备份为一个快照。即不想检测飞信的时候能够恢复原来的数据。
                     * 必须使用这里的Clone函数对每一项进行克隆,才能达到深度复制。否则ListViewItem作为引用类型,
                     * 使用直接赋值的话,控件里的项更改后,存在lstFeiXinBackup里面的项的值也会更改!*/
                    lstFeiXinBackup.Add((ListViewItem)tmp.Clone());

                    if ( tmp.SubItems[ 0 ].Text != "sms" )
                        continue;

                    if ( tmp.SubItems[ 1 ].Text.Contains("12520") )
                    {
                        tmp.SubItems[ 1 ].Text = tmp.SubItems[ 4 ].Text.Remove(tmp.SubItems[ 4 ].Text.IndexOf(':'));
                        tmp.SubItems[ 4 ].Text = tmp.SubItems[ 4 ].Text.Remove(0 , tmp.SubItems[ 4 ].Text.IndexOf(':') + 1);
                    }
                    else if ( tmp.SubItems[ 2 ].Text.Contains("12520") )
                        tmp.SubItems[ 2 ].Text = MyNickName.Text;//作为个人网名
                    else
                        continue;

                    //设置为飞信的时间格式。
                    tmp.SubItems[ 3 ].Text = tmp.SubItems[ 3 ].Text.Replace('.' , '-');
                    tmp.SubItems[ 3 ].Text += ":00";
                }

            }
            else
            {
                if ( lstFeiXinBackup.Count == 0 )
                    return;
                lsvMessageShower.Items.Clear();
                lsvMessageShower.Items.AddRange(lstFeiXinBackup.ToArray());
                //马上初始化此快照。
                lstFeiXinBackup.Clear();
            }
        }

其实我仅仅修改了一处的代码,那就是把原先直接由ListView控件的Items属性转换而来的数据,改为了在Foreach循环中,一条一条的Clone添加到变量中。

经过测试,完美解决问题

[By:Asion Tang]

2010年12月18日 22:01:40

posted @ 2010-12-17 20:45  Asion Tang  阅读(281)  评论(0编辑  收藏  举报