第7章 调试和错误处理 7.1.1 VS中的调试

VS有一个输出窗口,调用方法是

第7章 调试和错误处理         7.1.1 VS中的调试

需要注意的是,这个窗口有两个模式,使用其中的下拉列表就可以选择这些模式。可以在Build和Debug模式之间切换。Build和Debug模式分别显示编译和运行期间的信息。

本节提到的“写入Output窗口”时,实际上是指“写入Output窗口的Debug模式视图”。

另外,还可以创建一个日志文件,在运行应用程序时,会把信息添加到该日志文件中。把信息写入日志文件所使用的技巧与把文本写到Output窗口上所使用的技巧相同,但需要理解如何从C#应用程序中访问文件系统。现在,把这个功能放在本书后面的章节中,因为即使不太了解文件访问技巧,也可以把信息写入日志文件。

输出调试信息

在运行期间把文本写入Output窗口是非常简单的。只要用需要的调用替代Console.Writle()调用,就可以把文本写到希望的地方。此时,可以使用如下两个命令

Debug.WriteLine()    //将信息输出到Output窗口的Debug模式视图

Trace.WriteLine()      //将信息输出到Output窗口的Debug模式视图

此处的Debug和Trace和输出窗口中的Debug和Build无关,和项目的配置有关

第7章 调试和错误处理         7.1.1 VS中的调试

这两个命令函数的使用方法几乎完全相同,但有一个关键的区别。第一个命令仅在调试模式下运行,而第二个命令还可用于发布程序。

实际上,Debug.WriteLine()命令甚至不能编译为可发布的程序,在发布版本中,该命令会消失,这肯定有其优点(显而易见的是,编译好的代码文件比较小)。

实际上,一个源文件可以创建出两个版本的应用程序。调试版本显示所有的额外诊断信息,而发布版本没有这个功能,也不向用户显示信息,否则,用户会觉得这非常讨厌。

这两个函数的用法与Console.WriteLine()是不同的。其唯一的字符串参数用于输出消息,而不需要使用{X}语法插入变量值。这意味着必须使用+运算符在字符串中插入变量值。它们还可以有第二个字符串参数,用于显示输出文本的类别,这样,如果应用程序的不同地方输出了类似的消息,我们就可以马上确定Output窗口中显示的是哪些输出信息。

这些函数的一般输出如下所示:

<category>:<message>

例如,下面的语句把MyFunc作为可选的类别参数

Debug.WriteLine("Added 1 to i","MyFunc");

其结果为:

MyFunc:Added 1 to i  //MyFunc为category,Added 1 to i为message

下面的示例以这种方式输出调试信息。

class Ch07Ex01
    {
        public static void Method()
        {
            int[] testArray = { 4, 7, 4, 2, 7, 3, 7, 8, 3, 9, 1, 9 };//初始化一个测试用的整数数组
            int[] maxValIndices;//声明一个整数数组,用于存储Maxima函数的下标结果
            //调用函数
            int maxVal = Maxima(testArray,out maxValIndices);
            //输出返回结果
            Console.WriteLine("Maximum value {0} found at element indices.", maxVal);
            foreach (int index in maxValIndices)
            {
                Console.WriteLine(index);
            }
            Console.ReadKey();
        }

        /// <summary>
        /// 计算整数数组中的最大值
        /// </summary>
        /// <param name="integers">整数数组</param>
        /// <param name="indices">存储最大值的下标</param>
        /// <returns></returns>
        static int Maxima(int[] integers, out int[] indices)//最大数,极大值
        {
            Debug.WriteLine("Maximum value search started.");
            
            //开始搜索时,假定源数组中的第一个元素就是最大值,且数组中只有一个最大值
            indices = new int[1];
            int maxVal = integers[0];//将数组中的第一个元素赋予maxVal
            indices[0] = 0;//第一个元素的下标
            int count = 1;//存储搜索到的最大值的个数

            Debug.WriteLine(string.Format("Maximum value intialized to {0},at element index 0.", maxVal));//string.Format()比使用+连接运算符略高效些
            for (int i = 1; i < integers.Length; i++)//循环遍历整数数组,但忽略第一个元素,因为已经处理过
            {
                Debug.WriteLine(string.Format("Now looking at element at index {0}.", i));
                if (integers[i] > maxVal)//每个值都和maxVal比较
                {
                    maxVal = integers[i];//修改最大值
                    count = 1;
                    indices = new int[1];//修改下标数组
                    indices[0] = i;
                    Debug.WriteLine(string.Format("New maximum found. New value is {0},at element index {1}.", maxVal, i));
                }
                else
                {
                    if (integers[i] == maxVal)//找到和最大值相同的数据,记录index
                    {
                        count++;
                        int[] oldIndices = indices;//先备份旧的下标数组
                        indices = new int[count];//重建不同长度的数组来实现返回一个刚好能容纳搜索到的下标的数组

                        //CopyTo()函数:把oldIndices中的值复制到新的indices数组中,此函数只提取一个目标数组和一个用于复制第一个元素的下标
                        oldIndices.CopyTo(indices, 0);//把旧数组中的值复制到新的数组

                        indices[count - 1] = i;
                        Debug.WriteLine(string.Format("Duplicate maximum {0} found, with {1} occurrences", maxVal, count));
                    }
                }
            }
            Trace.WriteLine(string.Format("Maximum value {0} found,with {1} occurrences.", maxVal, count));//输出窗口中的Release模式下
            Debug.WriteLine("Maximum value search completed.");
            return maxVal;
        }
    }

 Debug模式下的输出窗口

第7章 调试和错误处理         7.1.1 VS中的调试

Release模式下的输出窗口

第7章 调试和错误处理         7.1.1 VS中的调试



除了WriteLine()函数外,还需要注意其他一些问题
1.首先是Console.WriteLine()的等价函数:Debug.Write()和Trace.Write()
 这两个函数使用与WriteLine()函数相同的语法(一个或两个参数,即一个消息和可选的类别),但它们还是有区别的,因为它们没有添加行尾字符

2.还有下述命令

Debug.WriteLineIf()

Trace.WriteLineIf()

Debug.WriteIf()

Trace.WriteIf()

这些函数的参数都与没有if的对应函数相同,但增加了一个必选参数,且该参数放在列表参数的最前面。这个参数的值为布尔值(或者计算结果为布尔值的表达式),只有这个值为true的之后,函数才会输出文本。使用这些函数可以有条件地把文本输出到Output窗口中。

例如,在某些情况下,只需输出调试信息,所以代码中有许多Debug.WriteLineIf()语句,它们都取决于具体的条件,如果没有这个条件,就不显示它们,以防Output窗口显示多余的信息。

1.跟踪点

设置好断点之后,在所在的行右键,选择断点,先插入断点

然后再右键,断点,命中条件

第7章 调试和错误处理         7.1.1 VS中的调试

在打开的“命中断点时”的对话框中,在打印消息中输入要处处的字符串,如果要输出变量值,可以把变量名放在花括号中

第7章 调试和错误处理         7.1.1 VS中的调试

2.诊断输出与跟踪点