C#学习笔记(十)-- 调试和错误处理

一、非中断模式下的调试

  非中断模式下的调试需要把调试信息输入到output窗口,在运行期间把文本写入output窗口是非常简单的,只需要将所需的调用代替WriteLine()的调用,此时可以使用如下两个命令:

Debug.WriteLine();
Trace.WriteLine();

  这两个命令函数的用法几乎完全相同,但有一个重要区别:第一个命令仅在调试模式下运行,而第二个命令还可以用于发布程序。Debug.WriteLine()和Trace.WriteLine()方法包含在System.Diagnostics名称空间中。

  这两个函数的用法与WriteLine()是不同的。其唯一的字符串参数用于输出消息,而不需要使用{X}语法插入变量值,这意味着必须使用+串联运算符等方式在字符串中插入变量值。它们还可以有第二个字符串参数(可选),用于显示输出文本的类别

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using static System.Console;

namespace Ch07Ex01
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] testArray = { 4, 7, 4, 2, 7, 3, 7, 8, 3, 9, 1, 9 };
            int maxVal = Maxima(testArray, out int[] maxValIndices);
            WriteLine($"Maximum value {maxVal} found at element indices:");
            foreach (int index in maxValIndices)
            {
                WriteLine(index);
            }
            ReadKey();
        }
        static int Maxima(int[] integers, out int[] indices)
        {
            Debug.WriteLine("Maximum value search started.");
            indices = new int[1];
            int maxVal = integers[0];//假定输入数组的第一个元素就是最大值
            indices[0] = 0;//indices数组用来记录最大值元素在数组中的索引
            int count = 1;//count用于记录搜索到的最大值个数
            Debug.WriteLine(string.Format(
               $"Maximum value initialized to {maxVal}, at element index 0."));
            for (int i = 1; i < integers.Length; i++)
            {
                Debug.WriteLine(string.Format(
                   $"Now looking at element at index {i}."));
                if (integers[i] > maxVal)
                {
                    //找到比上一个最大值大的元素时更新最大值及其索引
                    maxVal = integers[i];
                    count = 1;
                    indices = new int[1];
                    indices[0] = i;
                    Debug.WriteLine(string.Format(
                         $"New maximum found. New value is {maxVal}, at " +
                         $"element index {i}."));
                }
                else
                {
                    if (integers[i] == maxVal)
                    {
                        //找到和最大值相同大小的数值时
                        count++;//最大值计数个数加一
                        int[] oldIndices = indices;//保存旧的索引值
                        indices = new int[count];//新建一个索引值数组
                        oldIndices.CopyTo(indices, 0);//将旧索引值数组的所有元素复制到新的索引值数组中
                        indices[count - 1] = i;//更新新的索引值数组中的索引值
                        Debug.WriteLine(string.Format(
                           $"Duplicate maximum found at element index {i}."));
                    }
                }
            }
            Trace.WriteLine(string.Format(
               $"Maximum value {maxVal} found, with {count} occurrences."));
            Debug.WriteLine("Maximum value search completed.");
            return maxVal;
        }
    }
}

  在Debug模式下output窗口信息:

“Ch07Ex01.exe”(CLR v4.0.30319: DefaultDomain): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: DefaultDomain): 已加载“D:\Users\39881\Source\Repos\BeginningCSharp7\Chapter07\Ch07Ex01\bin\Debug\Ch07Ex01.exe”。已加载符号。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
Maximum value search started.
Maximum value initialized to 4, at element index 0.
Now looking at element at index 1.
New maximum found. New value is 7, at element index 1.
Now looking at element at index 2.
Now looking at element at index 3.
Now looking at element at index 4.
Duplicate maximum found at element index 4.
Now looking at element at index 5.
Now looking at element at index 6.
Duplicate maximum found at element index 6.
Now looking at element at index 7.
New maximum found. New value is 8, at element index 7.
Now looking at element at index 8.
Now looking at element at index 9.
New maximum found. New value is 9, at element index 9.
Now looking at element at index 10.
Now looking at element at index 11.
Duplicate maximum found at element index 11.
Maximum value 9 found, with 2 occurrences.
Maximum value search completed.

  在Release模式下output窗口的信息:

“Ch07Ex01.exe”(CLR v4.0.30319: DefaultDomain): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: DefaultDomain): 已加载“D:\Users\39881\Source\Repos\BeginningCSharp7\Chapter07\Ch07Ex01\bin\Release\Ch07Ex01.exe”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
模块“Ch07Ex01.exe”的符号未加载。

1. 使用调试生成配置或禁用调试选项“启用‘仅我的代码’”。
2. 检查调试选项下的“符号”设置。“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01.exe”(CLR v4.0.30319: Ch07Ex01.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
Maximum value 9 found, with 2 occurrences.

  在Release模式下不会编译Debug.WriteLine(),因此输出的信息也就减少了。

  另一种在output窗口输出信息的方式是增加跟踪点(TracePoint)

  1)将光标放在要插入跟踪点的代码行上,跟踪点会在执行这行代码之前被处理;

  2)单击行号左边的侧边栏,会出现一个红色的圆,将鼠标指针悬停在这个红色的圆上,选择Setting菜单项;

  3)选中操作复选框,在显示一条信息的文本框部分键入要输出的字符串,如果要输出变量值,应将变量名放在花括号中;

  4)单击OK后,在包含跟踪点的代码行左边的红色圆会变成一个红色菱形,该行突出显示的代码也会由红色变为白色。

  在Debug模式下运行,跟踪点显示的output信息与Debug.WriteLine()所显示的信息完全一致:

“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: DefaultDomain): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: DefaultDomain): 已加载“D:\Users\39881\Source\Repos\BeginningCSharp7\Chapter07\Ch07Ex01TracePoints\bin\Debug\Ch07Ex01TracePoints.exe”。已加载符号。
“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: Ch07Ex01TracePoints.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: Ch07Ex01TracePoints.exe): 已加载“D:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\PrivateAssemblies\Runtime\Microsoft.VisualStudio.Debugger.Runtime.Desktop.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
Maximum value initialized to 4,at element index 0
“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: Ch07Ex01TracePoints.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: Ch07Ex01TracePoints.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“Ch07Ex01TracePoints.exe”(CLR v4.0.30319: Ch07Ex01TracePoints.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
Maximum value 9 found, with 2 occurrences.

  诊断输出与跟踪点的区别:

  跟踪点由VisualStudio处理,在应用程序的Release版本中,跟踪点是不存在的,只有应用程序在VisualStudio的Debug模式下,跟踪点才有作用。跟踪点可以非常方便地添加额外信息,同时当觉得额外信息过于复杂时,也可以取消勾选跟踪点使其不再显示。

  诊断输出:总需要从应用程序中输出调试结果时使用这种方法,尤其是当输出的字符串比较复杂,涉及到几个变量或者许多信息的情况下,使用该方法比较合适。另外,如果要在执行发布版本的应用程序的过程中进行输出,Trace命令经常是唯一的选择。

  跟踪点:调试程序时,如果希望快速输出重要信息,以便消除语义错误,应使用跟踪点。

二、中断模式下的调试

  在Debug模式下,通过以下的三个按钮可以手动控制中断:

  使用它们可以:

  1)暂停应用程序的执行,进入中断模式;

  2)完全停止应用程序的执行,不进入中断模式,而是退出应用程序;

  3)重新启动应用程序。

  如果完全依靠暂停应用程序,会使其暂停的位置变得随机,一般情况下,使用断点

  断点是源代码中自动进入中断模式的标记,它们可以配置为:

  1)遇到断点时,立即进入中断模式

  2)遇到断点时,如果布尔表达式的值为true,就进入中断模式

  3)当遇到某一段点一定次数后,进入中断模式

  4)在遇到断点时,如果自从上次遇到断点以来的变量的值发生了变化,就进入中断模式

  注意:断点只适用于Debug模式,在Release版本中断点会被忽略。

  断点设置

  右击断点,在条件复选框中下拉菜单可以选择需要进行的操作:

  进入中断模式的其他方式

  生成一条判断语句时中断,与调试输出函数一样,判定函数也有两个版本:

  Debug.Assert();

  Trace.Assert();

  这两个函数带有3个参数:第一个参数是一个布尔值,当其值为false会触发判定语句;第二、第三个参数是两个字符串,分别把信息写到弹出的对话框和output窗口中,例如:

Trace.Assert(myVal < 10, "Variable out of bounds.", "Please contact vendor with the error code KCW001.");

  对于开发人员而言,可以很方便地找到错误的原因。

  通过监视变量的内容,单步执行代码,Immediate和Command窗口,以及Call Stack窗口有助于在调试过程中找到错误的源头。

三、错误处理

  try...catch...finally模块处理异常:

  1)try块在发生异常的地方中断程序的执行;

  2)如果有catch块,就检查该块是否匹配已抛出的异常类型。如果没有catch块,就执行finally块(如果没有catch块,就一定要有finally块);

  3)如果有catch块,但它与已发生的异常类型不匹配,就检查是否有其他的catch块

  4)如果有catch块匹配已发生的异常类型,且有一个异常过滤器是true,就执行这个catch块所包含的代码,再执行finally块(如果有的话);

  5)如果有catch块匹配已发生的异常类型,但没有匹配的异常过滤器,就执行finally块

  6)如果有catch块匹配已发生的异常类型,但没有异常过滤器,就执行这个catch块所包含的代码,再执行finally块

  7)如果catch块都不匹配发生的异常类型,就执行finally块(如果有的话)。

  注意:

  如果存在两个处理相同异常类型的catch块,就只执行异常过滤器为true的catch块中的代码。如果还存在一个处理相同异常类型的catch块,但没有异常过滤器或异常过滤器是false,就忽略它只执行一个catch块的代码,catch块的顺序不影响执行流

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;

namespace Ch07Ex02
{
    class Program
    {
        static string[] eTypes = { "none", "simple", "index",
                                 "nested index", "filter" };

        static void Main(string[] args)
        {
            foreach (string eType in eTypes)
            {
                try
                {
                    WriteLine("Main() try block reached.");        // Line 21
                    WriteLine($"ThrowException(\"{eType}\") called.");
                    ThrowException(eType);
                    WriteLine("Main() try block continues.");      // Line 24
                }
                catch (System.IndexOutOfRangeException e) when (eType == "filter")
                {
                    BackgroundColor = ConsoleColor.Red;
                    WriteLine("Main() FILTERED System.IndexOutOfRangeException" +
                              $"catch block reached. Message:\n\"{e.Message}\"");
                    ResetColor();
                }
                catch (System.IndexOutOfRangeException e)                 // Line 33
                {
                    WriteLine("Main() System.IndexOutOfRangeException catch " +
                              $"block reached. Message:\n\"{e.Message}\"");                
                }
                catch                                                     // Line 38
                {
                    WriteLine("Main() general catch block reached.");
                }
                finally
                {
                    WriteLine("Main() finally block reached.");
                }
                WriteLine();
            }
            ReadKey();
        }
        static void ThrowException(string exceptionType)
        {
            WriteLine($"ThrowException(\"{exceptionType}\") reached.");
            switch (exceptionType)
            {
                case "none":
                    WriteLine("Not throwing an exception.");
                    break;                                               // Line 57
                case "simple":
                    WriteLine("Throwing System.Exception.");
                    throw new System.Exception();                      // Line 60
                case "index":
                    WriteLine("Throwing System.IndexOutOfRangeException.");
                    eTypes[5] = "error";                                 // Line 63
                    break;
                case "nested index":
                    try                                                  // Line 66
                    {
                        WriteLine("ThrowException(\"nested index\") " +
                                          "try block reached.");
                        WriteLine("ThrowException(\"index\") called.");
                        ThrowException("index");                          // Line 71
                    }
                    catch                                                // Line 73
                    {
                        WriteLine("ThrowException(\"nested index\") general"
                                          + " catch block reached.");
                        throw;
                    }
                    finally
                    {
                        WriteLine("ThrowException(\"nested index\") finally"
                                          + " block reached.");
                    }
                    break;
                case "filter":
                    try                                                  // Line 86
                    {
                        WriteLine("ThrowException(\"filter\") " +
                                          "try block reached.");
                        WriteLine("ThrowException(\"index\") called.");
                        ThrowException("index");                          // Line 91
                    }
                    catch                                                // Line 93
                    {
                        WriteLine("ThrowException(\"filter\") general"
                                          + " catch block reached.");
                        throw;
                    }
                    break;
            }
        }
    }

}

  这段程序需要结合断点进行单步调试输出需要的信息。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#中,错误处理和异常处理是非常重要的,可以帮助我们优雅地处理运行时错误,并保护应用程序免受崩溃和不可预测的行为。以下是一些常见的C#错误处理和异常处理的技术和原则: 1. 异常(Exceptions):C#使用异常机制来处理运行时错误。当发生异常时,可以抛出(throw)异常,并使用try-catch语句来捕获(catch)和处理异常。 ```csharp try { // 可能会引发异常的代码 } catch (Exception ex) { // 处理异常的代码 } ``` 2. 异常类型(Exception Types):C#提供了许多内置的异常类型,例如`ArgumentException`、`InvalidOperationException`等。可以根据具体的情况选择合适的异常类型,或自定义异常类型。 ```csharp try { // 可能会引发异常的代码 } catch (ArgumentException ex) { // 处理特定类型的异常 } catch (Exception ex) { // 处理其他类型的异常 } ``` 3. finally块:除了try和catch块外,还可以使用finally块来确保无论是否发生异常,都会执行特定的代码。 ```csharp try { // 可能会引发异常的代码 } catch (Exception ex) { // 处理异常的代码 } finally { // 执行清理或资源释放的代码 } ``` 4. 自定义异常(Custom Exceptions):可以根据需要自定义异常类型,以便更好地表示特定的错误场景。 ```csharp public class MyCustomException : Exception { // 自定义异常的定义 } ``` 5. 异常处理最佳实践: - 只捕获你知道如何处理的异常,避免捕获所有异常。 - 在合适的层次捕获和处理异常,避免在每个方法中都使用try-catch块。 - 记录异常信息,以便进行故障排除和日志记录。 - 在catch块中避免使用空的catch语句,至少输出或记录异常信息。 - 使用finally块进行清理和资源释放操作。 - 使用using语句来确保及时释放使用了IDisposable接口的对象。 ```csharp try { using (var resource = new MyDisposableResource()) { // 使用资源的代码 } } catch (Exception ex) { // 处理异常的代码 } ``` 以上是一些常见的C#错误处理和异常处理的技术和原则。合理地使用异常处理机制可以提高应用程序的健壮性和可靠性,减少潜在的错误和问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值