在c#中有Exception
类。出于测试的目的,我希望能够将其StackTrace
属性设置为任意字符串。StackTrace
没有setter,所以我的第一次尝试是尝试使用反射:
Exception instance = new Exception("Testing");
PropertyInfo propertyInfo = typeof(Exception).GetProperty("StackTrace");
propertyInfo.SetValue(instance, "Sample StackTrace value", null);
这将产生运行时错误:
System.ArgumentException : Property set method not found.
是否有任何方法可以设置StackTrace
属性?更一般地说,有没有一种方法可以设置一个没有setter的属性?
您可以派生自己的异常类并覆盖StackTrace
属性,该属性是虚拟的:
public sealed class MyException: Exception
{
public MyException(string message, string stackTrace): base(message)
{
_stackTrace = stackTrace;
}
public override string StackTrace
{
get
{
return _stackTrace;
}
}
private readonly string _stackTrace;
}
请注意,要正确地做到这一点,您应该真正实现所有标准的异常构造函数,但对于单元测试,这可能不是严格必要的。
参见设计自定义异常了解更多细节:
这个属性是这样实现的:
public virtual string StackTrace
{
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return this.GetStackTrace(true);
}
}
被调用的方法是这样实现的:
private string GetStackTrace(bool needFileInfo)
{
string text = this._stackTraceString;
string text2 = this._remoteStackTraceString;
if (!needFileInfo)
{
text = this.StripFileInfo(text, false);
text2 = this.StripFileInfo(text2, true);
}
if (text != null)
{
return text2 + text;
}
if (this._stackTrace == null)
{
return text2;
}
string stackTrace = Environment.GetStackTrace(this, needFileInfo);
return text2 + stackTrace;
}
因此,如果您将字段_stackTraceString
设置为任何String
,您将得到_stackTraceString
+ _remoteStackTraceString
。
您可以使用FieldInfo.SetValue设置字段。
我通过http://ilspy.net得到了这个信息。
不要在生产环境中这样做。事物以特定的方式设计是有原因的,只是暴露的API是有保证的,一个小的升级可能会改变这个内部实现细节并破坏你的代码,所以永远不要依赖内部细节,只暴露的API。
欢呼。
您可以修改对象的后备字段:
Exception instance = new Exception("Testing");
var f = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField);
f.SetValue(instance, "hello");
这应该可以工作,要获得私有字段列表,您可以使用:
var fields = typeof(Exception).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
我个人更喜欢Matthew Watson提出的解决方案。
Exception对象的StackTrace属性是在堆栈遍历期间由运行时设置的,因此我们不能手动设置。