【问题标题】:How to set the InnerException of custom Exception class from its constructor如何从其构造函数设置自定义异常类的 InnerException
【发布时间】:2010-11-30 19:24:48
【问题描述】:

当我在该对象的构造函数中时,如何设置Exception 对象的InnerException 属性?这归结为查找和设置没有设置器的属性的支持字段。

顺便说一句:我已经看到了这个evain.net - Getting the field backing a property using Reflection,但如果可能的话,我正在寻找非基于 IL 的解决方案。

Exception的构造函数是创建Exception类型的地方,所以我不能使用基类构造函数MyException() :base(...)等来调用它。

【问题讨论】:

  • 重新跟进;然后(正如我提到的)在 ctor 的静态方法 ahead 中创建类型,而不是在构造函数主体内。
  • 在我的情况下封装更有意义。
  • ...但我同意你的观点,我会考虑把它变成工厂以避免反射成本。
  • (fwiw,我没有说工厂;看看这个例子——你可以使用静态方法来做就在基础ctor之前,在对base的调用中; 你把你的工作放在这里,然后)

标签: c# .net exception logging reflection


【解决方案1】:

在我的情况下,我使用了以下代码:

class Foo 
{
    void Bar(MyException myException = null)
    {
        try
        {
            SomeActions.Invoke();
        }
        catch (Exception ex)
        {
            if (myException != null)
            {
                // Here I regenerate my exception with a new InnerException
                var regenMyException = (MyException)System.Activator.CreateInstance(myException.GetType(), myException.Message, ex);
                throw regenMyException;
            }

            throw new FooBarException("Exception on Foo.Bar()", ex);
        }
    }
}

某人;)。

【讨论】:

    【解决方案2】:

    我知道我真的迟到了,但我发现这很管用。

    public class MyException: Exception
    {
        public void SetInnerException(Exception exception) 
        {
            typeof(Exception)
                .GetField("_innerException", BindingFlags.NonPublic | BindingFlags.Instance)
                .SetValue(this, exception);
        } 
    }
    

    诀窍是获取实际的Exception 类型的_innerException 字段,然后将内部异常值设置为您的类实例(在本例中为MyException)。

    这也适用于变量。

    Exception mainException = new Exception();
    typeof(Exception)
        .GetField("_innerException", BindingFlags.NonPublic | BindingFlags.Instance)
        .SetValue(mainException , new Exception("Something bad happened!"));
    

    【讨论】:

      【解决方案3】:

      使用 Redgate 的 Reflector 找到该字段。我怀疑 Exception 的实现会不会改变……但这是有风险的!

      【讨论】:

      • 你为什么会怀疑这一点?你有什么依据做这个评估?如果我是 Microsoft,我会更改实现,以对抗依赖其他人类的内部实现细节的傻瓜。
      • 基于在过去的框架更新中更改的此类详细信息的数量?
      • 真正的原因不是Exception 类的内部实现可能会改变。 (不能保证它不会,但没有理由相信它会。)相反,通过访问和修改私有字段,您违反了类不变量:只允许异常链接到现有异常并且永远不允许这种关联改变,保证异常链总是有限的并且永远不会递归循环到自身。
      【解决方案4】:

      为什么不能直接调用以 InnerException 作为参数的构造函数?如果由于某种原因不可能,System.Exception 中的支持字段是:

      private Exception _innerException;
      

      我使用Redgate's Reflector 发现了它。使用反射我想你可以设置内部异常。

      编辑:在大多数情况下,通过反射访问私有字段并不是一个好主意,但我对 NT 的情况了解不多,无法确定这是个好主意还是坏主意。

      【讨论】:

      • 我想不出一个合理的例子来说明反射是做到这一点的正确方法......
      • 这对于涉及激活异常的动态场景是有意义的,其中内部异常无法提前知道,因此无法传递给构造函数。
      • 不,它没有。真的没有;它破坏了封装。在` : base(SomeMethod(...), ...)`链中使用静态方法。
      • @_NT:为了使内部异常有意义,它应该发生在当前异常之前。通常,您希望将异常向上冒泡...而不是向下冒泡。
      • 顺便说一句...用反射改变私有和内部值可能会在未来引起重大变化。如果您这样做,后果自负。
      【解决方案5】:

      您通过调用基本 ctor 来设置内部异常:

      public MyException(string message, Exception innerException)
             : base(message, innerException) {...}
      

      如果需要运行一些代码来获取异常,请使用静态方法:

      public MyException(SomeData data) : base(GetMessage(data), GetInner(data)) {...}
      static Exception GetInner(SomeData data) {...} // <===== your type creation here!
      static string GetMessage(SomeData data) {...}
      

      【讨论】:

        【解决方案6】:

        如果我理解你的问题,你想做这样的事情:

        Exception ex = new Exception("test");
        Exception innerEx = new Exception("inner");
        ex.GetType().GetField("_innerException", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(ex, innerEx);
        

        如果您在从 Exception 继承的对象的构造函数中,您将使用 this 而不是第一个 ex

        但这可能不是处理您要处理的任何事情的最佳方式。

        【讨论】:

        • 异常有什么问题 innerEx = new Exception("inner");异常 ex = new Exception("test", innerEx); ?
        • 他试图修改不是在上下文中创建的对象ex,我创建它只是为了演示。实际上,在他的情况下,他正在尝试修改 this 实例。
        • +1:这回答了我自己的相当愚蠢的问题,即如何强制异常的内部异常成为自身,从而导致有趣的事情,例如让 ToString() 方法触发堆栈溢出。
        • @Brian:这正是 .NET Framework 不公开设置内部异常的原因。如果异常可以链接到现有异常,并且这种关联永远不会改变,则异常链保证是有限的。如果您可以更改内部异常,则无法再做出此保证 - 您可以将异常链递归循环到自身。
        【解决方案7】:

        Exception 类有一个接受内部异常作为参数的重载构造函数:

        Exception exc = new Exception("message", new Exception("inner message"));
        

        这是你要找的吗?

        【讨论】:

        • 虽然这并不能直接回答他的问题,但它回答了大多数搜索此内容的人正在寻找的内容。
        • @rollsch 是对的。
        【解决方案8】:

        在使用Exception(string, Exception) 构造函数时不应该设置InnerException 吗?我认为这是设计使然,否则您无法更改此设置,但您始终可以在构建时遵循适当的构造函数:

        class MyException : Exception {
            public MyException()
                : base("foo", new Exception("bar"))
            {
                ...
            }
        
            ...
        }
        

        我认为您永远不应该破坏代码中的异常链,因为这通常会导致您再也找不到错误。

        【讨论】:

          【解决方案9】:
          Exception exceptionWithMoreInfo = new Exception("extra info", ex);
          

          假设您已经捕获了一个异常,您希望在冒泡之前添加更多信息,这将是正常的做法。

          【讨论】:

          • 不过,这对异常的 ctor 没有帮助。
          猜你喜欢
          • 1970-01-01
          • 2013-07-15
          • 2013-01-26
          • 2010-11-07
          • 1970-01-01
          • 1970-01-01
          • 2011-10-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多