【问题标题】:How to get a value through a out/ref parameter from a method which throws an exception?如何从抛出异常的方法中通过 out/ref 参数获取值?
【发布时间】:2011-01-07 05:28:25
【问题描述】:

此代码输出“输出值”。

class P
{
  public static void Main()
  {
    string arg = null;
    try
    {
      Method(out arg);
    }
    catch
    {
    }
    Console.WriteLine(arg);
  }
  public static void Method(out string arg)
  {
    arg = "out value";
    throw new Exception();
  }
}

但这个没有。

class P
{
  public static void Main()
  {
    object[] args = new object[1];
    MethodInfo mi = typeof(P).GetMethod("Method");
    try
    {
      mi.Invoke(null, args);
    }
    catch
    {
    }
    Console.WriteLine(args[0]);
  }
  public static void Method(out string arg)
  {
    arg = "out value";
    throw new Exception();
  }
}

使用反射时,如何同时获得“输出值”和异常

【问题讨论】:

  • 好问题。但是如果方法抛出,你不应该依赖 out 值。
  • +1,好问题,当然我必须尝试一下:) 我推测您的原始变量没有传递给调用的函数,它得到一个副本,并且该副本得到成功完成后反射回原件(当然不会发生)。
  • @slugster:你的猜测是正确的。我怀疑没有任何方法可以通过反射来做到这一点。

标签: c# .net exception reflection byref


【解决方案1】:

异常绕过了 MethodInfo.Invoke() 中的代码,该代码将 [out] 值从堆栈帧复制回对象数组。 Invoke() 创建的堆栈帧上的值的行为就像它在第一个 sn-p 中一样。但这就是相似之处。

【讨论】:

    【解决方案2】:

    唯一的方法是以一种考虑异常可能性的方式重载您的方法,然后在“以防万一”中传递一个。以下产生了我认为你正在寻找的东西。据我了解,问题是反射不会直接操作通过引用传入的地址。在无一例外地到达方法终点之前,地址不会受到影响。可能是来自 MS 的内存保护或内存安全方案。

    class P
        {
            public static void Main()
            {
                object[] args = { "1", new Exception()};
                MethodInfo mi = typeof(P).GetMethod("Method");
                try
                {
                    mi.Invoke(null, args);
                }
                catch
                {
                }
                Console.WriteLine(args[0].ToString());
                Console.WriteLine(args[1].ToString());
            }
            public static void Method(ref string arg, ref Exception ex)
            {
                try
                {
                    arg = "out value";
                    throw new Exception();
                }
                catch (Exception exc)
                {
                    ex = exc;
                }
            }
    }
    

    【讨论】:

    • 我认为这并不能解决根本问题。如果我们对调用的方法有这种控制,我们就不会使用反射。关键是,这种方法可能存在于其他地方,我们可能需要使用反射来调用它。我们将如何做到这一点?我认为这是不可能的。
    • 我同意以所描述的方式不可能。我从帖子中了解到他确实可以控制反射方法,所以我想我会提出一个解决方法。
    【解决方案3】:

    我建议将方法更改为返回 Result 对象而不是 out 参数。 结果对象可以包含异常以及您的 arg 的值。

    【讨论】:

    • 正如我在@Joel 回答中的评论中指出的那样,这个问题是一个基本问题。如果我们对目标方法没有任何控制权怎么办。有一个任意方法,我们需要使用反射来调用它。我们将如何做到这一点?
    【解决方案4】:

    如果问题是,您如何捕捉到发生了异常并且您正在使用 Windows 窗体应用程序,您是否尝试过查看线程异常事件并将其与 SetUnhandledExceptionMode() 结合使用?

    Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    
    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)        
    {            
        HandleException(e.Exception);        
    }
    

    【讨论】:

    • 输出参数的值在哪里?我想你误解了这个问题。这不是关于捕捉异常。问题是,在使用反射的时候,如果被调用者抛出,输出参数的值是没有赋值的。这并不能解决问题。
    【解决方案5】:

    如果方法抛出异常,则 out 参数未定义。您可以通过在第一个示例中不将其初始化为 null 来看到这一点,然后代码将无法编译。

    因此,如果 Invoke 方法抛出异常,则不返回未定义的值是有意义的。

    【讨论】:

    • "如果方法抛出异常,out 参数未定义。"你能引用这个规范吗? “您可以通过在第一个示例中不将其初始化为 null 来看到这一点,然后代码将无法编译。”这不是结果“不确定性”的证明。它仅证明它不是根据 C# 编译器规则“明确分配”的。 “未明确分配”并不意味着未定义或未分配。这是完全不同的事情。
    • >> "如果方法抛出异常,则 out 参数未定义。"这是为什么?是的,如果您删除初始化,那么代码将无法编译。但不是因为 Method() 中存在 throw,而是因为 Main 中的空 catch 块,即并非所有执行路径在实际使用之前都会初始化 arg 的值。
    • -1,out var 不是未定义的 - 运行代码,你会看到。
    • @Mehrdad:未定义与未分配不同。
    • @Igor:是的,是未分配值的执行路径使代码无法编译,但是 try...catch 与 out 参数的返回方式无关。不管有没有try...catch,方法总是可以抛出异常,这样执行路径就一直存在。
    猜你喜欢
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    • 2019-08-12
    • 2019-04-24
    • 1970-01-01
    相关资源
    最近更新 更多