C# - 在 属性 setter 中抛出的异常在通过反射 (SetValue()) 调用时无法捕获

C# - Exception thrown in property setter cannot be caught when called via reflection (SetValue())

我的问题与Using reflection, setter throws exception which can't be caught类似,但是推荐的方案没有解决我的问题。

在下面的代码中,我尝试通过两种不同的方式设置 class 的 属性:第一种,直接,第二种,通过反射。 setter 抛出异常(在实际用例中,仅当给定值无效时)应在调用 setter 的地方捕获。 但是异常只在第一个变体中被捕获; 在第二个中,catch 块永远不会执行。 这是什么原因?

在我的用例中,我想使用反射变体,但程序总是在抛出异常的地方终止,我没有更改来捕获它。

上面提到的线程建议将调试器配置为在每次异常时中断(这不可能像第一个变体那样,异常被完美捕获)或者异常来了从别的地方。不幸的是,我不知道它可能来自哪里。

非常感谢您的帮助!

using System;
using System.Reflection;

namespace ReflectionTestConsole
{
    public class Program
    {
        public static void Main(string[] args)
        {

            Logic logic = new Logic();
            try
            {
                logic.MyProperty = 5;
            } 
            catch(Exception e)
            {   // in this case, the exception is caught here.
                Console.WriteLine("Exception was caught: " + e.Message);
            }

            try{
                PropertyInfo propInfo = logic.GetType().GetProperty("MyProperty");
                propInfo.SetValue(logic,5);
            }
            catch (Exception e)
            {   // in this case, the exception is not caught
                Console.WriteLine(e.Message);
            }

        }
    }

    public class Logic
    {
        private double variable;
        public double MyProperty
        {
            get
            {
                return variable;
            }
            set
            { //Check if value is valid and if not throw an exception
                throw new Exception("This is the exception");
            }
        }
    }
}

您的异常被抛出。但是因为它发生在 SetValue 方法内部。异常在 InnerException 内。要获得特定的异常,您需要调用 e.InnerException.

    catch (Exception e)
    {   
        Console.WriteLine("Reflection exception was caught: " + e.InnerException.Message);
    }

在简历中,您的 Exception e 是由 SetValue 方法引发的异常。由于另一个异常而上升,另一个异常是 InnerException.

您可以在以下位置查看工作示例:https://dotnetfiddle.net/i49ZQC

使用@Lutti Coelho 和@HimBromBeere 的评论以及这个 thread 我找到了解决问题的方法:

1) 我假设第二个异常(在反射调用中抛出)没有被捕获,这是错误的。它被捕获了,但我的控制台输出没有正确显示它。

2) SetValue()(或 GetValue())反射调用中抛出的每个异常都包含在 TargetInvocationException e 中。真正的例外是 e.InnerException。如果您抛出用户定义的异常并为此特定异常类型编写 catch 子句,这一点很重要。

明白了这两点,代码运行就如愿上了dotnetfiddle。然而,在 Visual Studio 中,调试器仍然在抛出异常的地方终止,告诉我异常未处理,即使我在反射调用周围放置了一个 try-catch-block。

3) 在这里,提到的 thread 帮助了我:Visual Studio 有一个名为 "Just my Code" 的设置(请参阅工具 → 选项 → 调试 → 常规 → 仅启用我的代码)。如果启用,它会在我的代码中未处理的每个异常时停止。在我的例子中,我的代码抛出了异常,但它让我的代码通过 C# 标准反射库并且稍后才返回到我的代码。由于异常在它离开我的代码时未得到处理,因此调试器在此时终止。 因此,在 Visual Studio 中禁用此设置最终解决了我的问题。