首页 > 编程语言 >WPF/C#:异常处理

WPF/C#:异常处理

时间:2024-06-12 11:44:34浏览次数:25  
标签:thread C# UI catch 线程 WPF 异常

什么是异常?

在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)]

标签:thread,C#,UI,catch,线程,WPF,异常
From: https://www.cnblogs.com/mingupupu/p/18243650

相关文章

  • WPF/C#:程序关闭的三种模式
    ShutdownMode枚举类型介绍ShutdownMode是一个枚举类型,它定义了WPF应用程序的关闭方式。这个枚举类型有三个成员:OnLastWindowClose:当最后一个窗口关闭或者调用System.Windows.Application.Shutdown方法时,应用程序会关闭。OnMainWindowClose:当主窗口关闭或者调用System.Windows.......
  • NETCORE - Grpc 通信四种方式
    NETCORE-Grpc通信四种方式 项目搭建:https://www.cnblogs.com/1285026182YUAN/p/17679730.html GRPC支持四种通信方式,分别是:UNARY:这是一种最简单的客户端与服务端模式,其中客户端发送一个消息后,阻塞等待服务端回复一个消息。CLIENTSTREAMING:在这种模式下,客户端将......
  • C. Ladder
    原题链接题解找到每一个点右边能递增多远和左边能递增多远code#include<bits/stdc++.h>usingnamespacestd;inta[100005],r[100005],l[100005];intmain(){intn,m;cin>>n>>m;for(inti=1;i<=n;i++)cin>>a[i];r[n]=n;for(inti=n-1;i>......
  • Arcee’s MergeKit: A Toolkit for Merging Large Language Models
    本文是LLM系列文章,针对《Arcee’sMergeKit:AToolkitforMergingLargeLanguageModels》的翻译。Arcee的MergeKit:一个用于合并大型语言模型的工具包摘要1引言2背景和相关工作3库设计:关键设计原则4MergeKit的可扩展性5MergeKit的普及性和有效性6结论和......
  • acwing 247 亚特兰蒂斯
    https://www.acwing.com/problem/content/249/有几个古希腊书籍中包含了对传说中的亚特兰蒂斯岛的描述。其中一些甚至包括岛屿部分地图。但不幸的是,这些地图描述了亚特兰蒂斯的不同区域。您的朋友Bill必须知道地图的总面积。你自告奋勇写了一个计算这个总面积的程序。输......
  • mongodb的安装使用、mongodb与redis,memcache,mysql的区别优缺点 以及 好用的MongoDB
    一、mongodb的安装使用、与redis,memcache,mysql的区别优缺点    MongoDB是一个介于关系数据库和非关系数据库之间的基于分布式文件存储的数据库。是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数......
  • CoLLEGe: Concept Embedding Generation for Large Language Models
    本文是LLM系列文章,针对《CoLLEGe:ConceptEmbeddingGenerationforLargeLanguageModels》的翻译。CoLLEGe:大型语言模型的概念嵌入生成摘要1引言2相关工作3CoLLEGe:概念学习与语言嵌入生成4用于训练CoLLEGe的数据集5实验6结论和讨论摘要当前的语言......
  • 第二章_Docker镜像操作
            Docker运行容器前需要本地存在对应的镜像,如果不存在本地镜像,Docker就会尝试从默认镜像仓库https;//hub,docker,com下载.这是由Docker官方维护的一个公共仓库,可以满足用户的绝大部分需求,用户也可以通过配置来使用自定义的镜像仓库。        下面......
  • 这才是CSDN最系统的网络安全学习路线(建议收藏)
      01什么是网络安全网络安全可以基于攻击和防御视角来分类,我们经常听到的“红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性,例如Web安全技术,既有Web渗透,也......
  • 这才是CSDN最系统的网络安全学习路线(建议收藏)
      01什么是网络安全网络安全可以基于攻击和防御视角来分类,我们经常听到的“红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性,例如Web安全技术,既有Web渗透,也......