在C#中,我试图修改我创建的自定义异常的StackTrace
属性。为此,我只是覆盖了StackTrace
getter,它似乎正在运行(Exception
将它作为virtual
方法,所以它应该可以工作)。这是我的自定义异常:
class CustomTimeoutException : Exception
{
private string oldStack;
public CustomTimeoutException(string message, String stack)
: base(message)
{
oldStack = stack;
}
public override string StackTrace
{
get
{
return oldStack;
}
}
public override string Message
{
get
{
return "lalalala2";
}
}
public override System.Collections.IDictionary Data
{
get
{
return null;
}
}
}
我通过以下方式使用此课程:
class Program
{
static void Main(string[] args)
{
try
{
Program p = new Program();
p.throwTimeout2();
}
catch(TimeoutException e)
{
CustomTimeoutException tor = new CustomTimeoutException(e.Message + "with more", e.StackTrace);
Console.WriteLine(tor);
Console.WriteLine(tor.StackTrace);
throw tor;
}
}
public void throwTimeout2()
{
throwTimeout();
}
public void throwTimeout()
{
throw new TimeoutException("this is a message ");
}
}
当我将属性StackTrace
写入控制台时,它会打印StackTrace
的{{1}},这意味着它实际上会覆盖它。但是,当控制台显示错误时,TimeoutException
不是从我的媒体获取的错误,但StackTrace
是从Message
属性获取的Message
我是也是压倒一切。
那么,控制台从何处获取异常的StackTrace
?我正在使用Visual Studio进行运行。
答案 0 :(得分:0)
(注意:有一点我仍然不明白你的问题是,虽然你似乎想要一种方法来影响如何向控制台报告异常,你也说你有一些"工具"显然可以访问Exception
对象本身,并报告最内层的异常,即在InnerException
属性链之后,直到它到达最后一个这两个陈述似乎彼此矛盾,所以我不确定我是否完全理解这个问题。但是,我花了一些时间研究它,并将分享我所拥有的。:)。) 的
恕我直言,没有一个好方法可以做你想要的。有一种丑陋的方式(见下文),但我不推荐它。特别是因为你的主要限制是"tool that uses the message";恕我直言,改变生产代码只是为了适应某些工具,这是一个非常的坏主意。如果不是彻头彻尾的错误代码,那条路会导致严重的维护问题。
如果您确实必须更改异常消息,您应该只是咬住子弹并将要保留的整个堆栈跟踪文本折腾到消息中。试图弄乱最终抛出的异常本身的堆栈跟踪是坏消息。
那说......
如果要将Console.WriteLine()
的诊断调用添加到方法覆盖中,您将确认(因为您可能已经猜到)在重新抛出异常时未调用StackTrace
属性并由CLR报告给控制台。覆盖该物业无济于事。
尝试该策略至少有两个问题:
StackTrace
属性只是一个string
,包含有关堆栈跟踪的格式化信息。 Exception
对象确实包含私有字段_stackTrace
。当您使用throw
对象执行Exception
语句时,CLR会将当前堆栈跟踪信息存储到此字段中。事实上你必须找到一些方法来覆盖这个字段,以伪造一个不同于实际的网站。Exception
对象来确定抛出站点;堆栈仍然存在,可以直接报告其状态。如果确实异常处理代码直接查看堆栈状态,则可以使用Exception
对象来实现 nothing ,这可能会影响报告。现在,实际上,对于我在Windows 8.1计算机上运行的.NET版本,似乎CLR正在报告存储在Exception
对象和不直接检查堆栈,按照上面的选项#2。所以这样的事情确实有效:
ExceptionWrapper类
class ExceptionWrapper : Exception
{
private readonly string _trace;
private readonly string _message;
private readonly string _toString;
private readonly object _stackTrace;
public ExceptionWrapper(Exception e)
{
_trace = e.StackTrace;
_message = e.Message + ", wrapped";
_toString = e.ToString();
_stackTrace = typeof(Exception).GetField("_stackTrace",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(e);
}
public override string StackTrace { get { return _trace; } }
public override string Message
{
get
{
typeof(Exception)
.GetField("_stackTrace", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(this, _stackTrace);
return _message;
}
}
public override string ToString() { return _toString; }
}
测试计划
class Program
{
static void Main(string[] args)
{
try
{
M1();
}
catch (Exception e)
{
throw;
}
}
private static void M1()
{
try
{
M();
}
catch (Exception e)
{
throw new ExceptionWrapper(e);
}
}
static void M()
{
throw new Exception("test exception");
}
}
但有一个非常重要的警告。这只是因为两个具体事实:
Exception
对象以在_stackTrace
字段中包含正确的堆栈信息。Message
对象的Exception
getter。这使得对象有机会在CLR访问它之前覆盖_stackTrace
字段,以便报告堆栈跟踪。就我所知,这些都是完全无证的实施细节。第二个特别是非常脆弱;虽然我可以看到为什么CLR可能总是必须坚持第一个细节,但我认为没有理由认为微软(更不用担心CLS的其他实现者)应该感到被迫在第二点保留行为。
编写依赖于这些实现细节的代码只是在寻找麻烦。无论这里的目标是什么,我都无法想象为了满足这种需求而遭受破坏项目的风险是值得的。恕我直言,简单地重写这个"工具"会更合理。你正试图容纳。
工具本质上(或者至少应该是)本质上是简单的,并且不仅可以重写它,而且可以产生您想要的结果,而无需在第三方代码中查找私有实现细节,它还可以为您提供有机会 a)添加新功能,例如提供控制如何处理异常数据的选项(例如,查看除最内层异常之外的其他内容),以及 b)有一个你做的工具有源代码,你可以用它来进一步改进。
我强烈建议你不要把自己画在这个角落里。 :)