WPF/C#:异常处理

什么是异常?

在C#中,异常是在程序执行过程中发生的特殊情况,例如尝试除以零、访问不存在的文件、网络连接中断等。这些情况会中断程序的正常流程。

当C#程序中发生这种特殊情况时,会创建一个异常对象并将其抛出。这个异常对象包含了关于异常的详细信息,如异常类型和异常发生时的程序状态。

异常处理是一个重要的编程概念,它允许程序员在异常发生时采取适当的行动,而不是让程序崩溃。在C#中,我们使用try,catch和finally关键字来处理异常。

比如,下面的代码用0除一个数时,会出现一个异常:

internal class Program
{
    static void Main(string[] args)
    {
        int x = 10, y = 0;
        x /= y;
    }
}

image-20240612074703674

try语句

try语句用来指明为避免出现异常而被保护的代码段,并在发生异常时提供代码处理异常。try语句由3个部分组成,如下图所示:

image-20240612081134390

处理异常

上面除以0会导致一个异常的程序,可以进行如下改写,进行异常处理:

internal class Program
{
    static void Main(string[] args)
    {
        int x = 10;
        try
        {
            int y = 0;
            x /= y;
        }
        catch
        {
            Console.WriteLine("Handling all exceptions");
        }
    }
}

异常被捕获:

image-20240612081753905

异常类

在C#中,所有的异常都是派生自System.Exception类的。System.Exception类是所有异常的基类,它提供了一些基本的功能,如返回错误消息和记录异常发生的堆栈跟踪。
C#提供了一些内置的异常类,用于表示常见的异常情况。例如:

  • System.NullReferenceException:当你试图访问一个null对象的成员时,会抛出这个异常。
  • System.DivideByZeroException:当你试图除以零时,会抛出这个异常。
  • System.IndexOutOfRangeException:当你试图访问数组或集合的无效索引时,会抛出这个异常。
  • System.IO.FileNotFoundException:当试图打开的文件不存在时,会抛出这个异常。

当一个异常发生时,CLR会创建该类型的异常对象,并寻找适当的catch子句处理它。

所有异常类从根本上派生自System.Exception类,异常继承层次的一个部分如下所示:

image-20240612083327072

catch子句

catch子句处理异常。它有3种形式,允许不同级别的处理,如下图所示:

image-20240612085205550

  • 一般catch子句:这种形式的catch子句可以捕获任何类型的异常。它不指定异常类型,所以它会捕获try块中抛出的所有异常。
  • 特定catch子句:这种形式的catch子句只捕获指定类型的异常。如果try块中抛出的异常类型与catch子句中指定的类型匹配,那么就会执行这个catch子句。
  • 带对象的特定catch子句:这种形式的catch子句不仅指定了异常类型,还定义了一个异常对象。这个异常对象可以用来访问关于异常的更多信息,如错误消息和堆栈跟踪。

将开头的例子,修改为使用带对象的特定catch子句,如下所示:

 internal class Program
 {
     static void Main(string[] args)
     {
         int x = 10;
         try
         {
             int y = 0;
             x /= y;
         }
         catch(DivideByZeroException e)
         {
             Console.WriteLine($"Message:{e.Message}");
             Console.WriteLine($"Source: {e.Source}");
             Console.WriteLine($"Stack: {e.StackTrace}");
         }         
     }
 }

输出结果如下所示:

image-20240612090547534

抛出异常

可以使用throw语句使代码显式地引发一个异常。throw语句的语法如下:

throw ExceptionObject;

好了,以上就是C#中关于异常处理的基础知识,现在我们结合WPF中的例子说明在WPF中如何进行异常处理的。

WPF中的异常处理

现在来看看ExceptionHandlingSecondaryUIThread这个例子,项目结构如下图所示:

image-20240612093133247

先来看一下这个项目的运行效果:

这个例子介绍了WPF中如何处理在辅助UI线程中发生的异常。

现在来看看是如何处理的。

StartSecondaryUIThreadButton按钮点击事件处理程序:

  private void startSecondaryUIThreadButton_Click(object sender, RoutedEventArgs e)
  {
      // Creates and starts a secondary thread in a single threaded apartment (STA)
      var thread = new Thread(MethodRunningOnSecondaryUIThread);
      thread.SetApartmentState(ApartmentState.STA);
      thread.IsBackground = true;
      thread.Start();
  }

这段代码的主要目的是创建并启动一个新的辅助UI线程。

  thread.SetApartmentState(ApartmentState.STA);

这行代码设置了线程的公寓状态为STA(Single-Threaded Apartment)。在WPF中,所有的UI线程都必须是STA线程,因为UI元素不是线程安全的。

Single-Threaded Apartment(STA)介绍

在WPF(Windows Presentation Foundation)中,Single-Threaded Apartment(STA)是指一个线程模型,其中每个线程都维护自己的消息队列,并且所有的UI操作都在这个线程上进行。
在STA模型中,每个线程都有自己的内存空间,这意味着线程之间的数据不会共享,从而避免了多线程编程中的许多并发问题。这对于UI编程来说非常重要,因为UI元素通常不是线程安全的,所以所有的UI操作都必须在同一个线程上进行。
在WPF中,主UI线程默认就是一个STA线程。此外,你也可以创建其他的STA线程,但是每个STA线程都只能操作它自己创建的UI元素。

MethodRunningOnSecondaryUIThread方法如下所示:

 // THIS METHOD RUNS ON A SECONDARY UI THREAD (THREAD WITH A DISPATCHER)
 private void MethodRunningOnSecondaryUIThread()
 {
     var secondaryUiThreadId = Thread.CurrentThread.ManagedThreadId;
     try
     {
         // On secondary thread, show a new Window before starting a new Dispatcher
         // ie turn secondary thread into a UI thread
         var window = new SecondaryUIThreadWindow();
         window.Show();
         Dispatcher.Run();
     }
     catch (Exception ex)
     {
         // Dispatch the exception back to the main ui thread and reraise it
         Application.Current.Dispatcher.Invoke(
             DispatcherPriority.Send,
             (DispatcherOperationCallback) delegate
             {
                 // THIS CODE RUNS BACK ON THE MAIN UI THREAD
                 string msg = $"Exception forwarded from secondary UI thread {secondaryUiThreadId}.";
                 throw new Exception(msg, ex);
             }
             , null);

         // NOTE - Application execution will only continue from this point
         //        onwards if the exception was handled on the main UI thread
         //        by Application.DispatcherUnhandledException
     }
 }

在try语句块中创建了SecondaryUIThreadWindow。

SecondaryUIThreadWindow上的按钮的点击事件处理程序如下所示:

 private void raiseExceptionOnSecondaryUIThreadButton_Click(object sender, RoutedEventArgs e)
 {
     // Raise an exception on the secondary UI thread
     string msg = $"Exception raised on secondary UI thread {Dispatcher.Thread.ManagedThreadId}.";
     throw new Exception(msg);
 }

抛出了一个异常。

这个异常被MethodRunningOnSecondaryUIThread方法中的catch子句段捕获:

image-20240612095805302

 Application.Current.Dispatcher.Invoke(
     DispatcherPriority.Send,
     (DispatcherOperationCallback) delegate
     {
         // THIS CODE RUNS BACK ON THE MAIN UI THREAD
         string msg = $"Exception forwarded from secondary UI thread {secondaryUiThreadId}.";
         throw new Exception(msg, ex);
     }
     , null);

这段代码的主要目的是在辅助UI线程上捕获异常,并将异常转发到主UI线程上进行处理。

在app.xaml中

  DispatcherUnhandledException="App_DispatcherUnhandledException"

这行代码是在WPF应用程序中设置全局未处理异常的处理器。

当应用程序的主调度器捕获到未处理的异常时,App_DispatcherUnhandledException方法会被调用来处理这个异常。
这是一种处理全局未处理异常的方式,可以防止应用程序因为未处理的异常而崩溃。在App_DispatcherUnhandledException方法中,你可以记录异常信息,显示错误消息,或者决定是否让应用程序继续运行。

   private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
   {
       // Display exception message
       var sb = new StringBuilder();
       sb.AppendFormat("{0}\n", e.Exception.InnerException.Message);
       sb.AppendFormat("{0}\n", e.Exception.Message);
       sb.AppendFormat("Exception handled on main UI thread {0}.", e.Dispatcher.Thread.ManagedThreadId);
       MessageBox.Show(sb.ToString());

       // Keep application running in the face of this exception
       e.Handled = true;
   }

这样就完成了在WPF中的辅助UI线程的异常处理。

还有一个例子是ExceptionHandlingSecondaryWorkerThread说明在WPF中如何处理在辅助工作线程(Secondary Worker Thread)中发生的异常,与这个例子类似。

参考

1、《C#图解教程》

2、[WPF-Samples/Application Management/ExceptionHandlingSecondaryUIThread at main · microsoft/WPF-Samples (github.com)](https://github.com/microsoft/WPF-Samples/tree/main/Application Management/ExceptionHandlingSecondaryUIThread)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/705709.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Go微服务框架Kratos中makefile命令的使用方法及报错处理

运用 kratos 微服务框架开发项目时,可以使用提供的 makefile 中的命令自动且快速生产相关代码,提高开发效率。 krotos中makefile文件内容如下: GOHOSTOS:$(shell go env GOHOSTOS) GOPATH:$(shell go env GOPATH) VERSION$(shell git descri…

翻译英文文献工具

翻译英文文献工具 1. zotero1. 安装zotero2. 安装pdf翻译器2.1. 查看zotero版本2.2. 下载对应的pdf翻译器插件版本2.3. 安装插件2.4. 使用插件 2. 小绿鲸英文文献阅读器1. 安装2. 使用 1. zotero 1. 安装zotero 官网: https://www.zotero.org/ 一键next默认安装就…

(四)React组件、useState、组件样式

1. 组件 1.1 组件是什么 概念:一个组件就是用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以相互嵌套,也可以复用多次。 组件化开发可以让开发者像搭积木一样构建一个完整的庞大应用 1.2 React组件 在React中&#xf…

独具韵味的移动端 UI 风格

独具韵味的移动端 UI 风格

《转载》前苏联的三进制计算机Setun

1、苏联的三进制计算机概述 早在 1956 年,就需要创建一种可在大学和实验室中使用的实用数字计算机模型。为此,需要一种易于学习、可靠、廉价但同时高效、专为大规模使用而设计的小型计算机。 对这种机器的要求:运行速度必须等于每秒数百次操作…

求单源最短路径的新方法

参见:dijkstra 算法为什么高效。 本来不想谈算法,本来只想了一下 dijkstra 算法背后的形而上,但还是归纳出一个仅靠一次广度优先遍历就能获得单源最短路径的新算法,框图里是算法流程,流程下是一个例子: …

Ubuntu Server 20.04挂载磁盘

先查看磁盘信息: sudo fdisk -l然后提供NTFS文件系统支持: sudo mkfs.ntfs /dec/sda -F这个过程非常久… 处理完如上图。(ps. 这个 Have a nice day. 好浪漫~) 接着挂载磁盘: sudo mount /dev/sda ~/device设置开机…

系统架构设计师 - 操作系统(2)

操作系统 操作系统(5-6分)文件管理绝对路径与相对路径 ★索引文件 ★★★位示图 ★ 存储管理段页式存储 ★★★页式存储段式存储段页式存储(了解) 页面置换算法 ★ 微内核操作系统嵌入式操作系统 ★★★ 大家好呀!我是…

第五十一天 | 1143.最长公共子序列

题目:1143.最长公共子序列718.最长重复子数组的区别是,子序列不要求连续,子数组要求连续。这一差异体现在dp数组含义和递推公式中,本题是子序列,那就要考虑上nums1[i - 1] ! nums2[j - 1]的情况。 本道题与 1.dp数组…

vue 应用测试(一) --- 介绍

vue 应用测试(一) ---介绍 前端测试简介组件测试Jest 测试框架简介其他测试框架 第一个测试避免误报如何组织测试代码 组件挂载Vue2 组件挂载的方式Vue3 的挂载方式vue-test-utils挂载选项 如何调试测试用例参考小结 前端测试简介 软件测试:…

【每日一练 】day2

✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 🎈🎈作者主页: 🎈丠丠64-CSDN博客🎈 ✨✨ 帅哥美女们,我们共同加油!一起…

企业多云策略的优势与实施指南

企业在选择云服务提供商时,常见的选项包括亚马逊AWS、微软Azure、谷歌云GCP、阿里云、腾讯云和华为云。为了避免过度依赖单一供应商,许多企业选择采用多云策略,这样可以充分利用不同云服务的优势,同时避免重复工作和其他额外的工作…

大模型应用开发课程上新!

在人工智能快速发展的今天,大模型应用已逐渐渗透到各个行业,对我们的工作和生活产生了深远的影响。越来越多的企业和开发者渴望深入探索大模型落地应用,然而却缺少高质量且专业的培训课程及学习途径。 为满足企业和开发者在实际场景中使用大…

ROS——自定义话题消息和使用方法

定义Person话题 定义Person发布者 /*** 该例程将发布/person_info话题&#xff0c;自定义消息类型&#xff1a; test_topic::Person*/#include <ros/ros.h> #include <test_topic/Person.h> //包含的头文件&#xff0c;ros相关的头文件&#xff0c;及自定义头文件…

单点登录分析介绍

文章目录 1、单点登录解决方案1.1、后端保存登录状态1.2、token模式 2、user服务-登录接口2.1、UserController2.2、UserInfoServiceImpl2.3、载荷2.4、响应2.5、Redis Desktop Manager 1、单点登录解决方案 多个系统只有一个登录服务 1.1、后端保存登录状态 1.2、token模式 …

Excel根据身份证号提取信息

概览 本篇文章主要对根据身份证号码提取出生年月日、年龄、性别、退休年龄这三项进行讲解。 一. 提取出生年月日 公式&#xff1a;TEXT(MID(B2,7,8),“0000-00-00”) MID(B2,7,8)&#xff1a;表示从单元格 B2 中的字符串&#xff08;这里是身份证号&#xff09;&#xff0c…

Day 43 keepalived高可用集群

keepalived高可用集群 负载均衡 lb集群 load balance ​ 流量分发 高可用 ha集群 high availability ​ 主要是给服务器做冗余 keepalive 持久连接 保持存活 keepalived 高可用软件名称 红帽有自己的高可用集群套件&#xff1a;RHCS keepalived介绍 ​ keepalived是集…

世优波塔AI数字人多模态交互系统,让智慧教育多维度落地应用

“你是谁&#xff0c;你能做什么&#xff1f;”、“降落伞应该包含哪些部分&#xff1f;”、“制作降落伞需要什么流程&#xff1f;”在北京四中雄安校区的实践课堂上&#xff0c;一个数字人AI一体机在教室中央引人注目&#xff0c;老师和学生们争相向名叫“小优”的“数字老师…

git如果将多次提交压缩成一次

将N个提交压缩到单个提交中有两种方式&#xff1a; git reset git reset的本意是版本回退&#xff0c;回退时可以选择保留commit提交。我们基于git reset的作用&#xff0c;结合新建分支&#xff0c;可以实现多次commit提交的合并。这个不需要vim编辑&#xff0c;很少有冲突。…

GTX的64B66B编码(高速收发器十九)

点击进入高速收发器系列文章导航界面 前文讲解了8B10B的原理&#xff0c;8B10B的开销比较大&#xff0c;每传输10位数据&#xff0c;就需要发送2位无效数据。为了减小8B10B编码的开销&#xff0c;同时保留编码方案的优点&#xff0c;提出了64B66B编码。 64B66B编码与8B10B编码方…