C# 捕获/修改(消息)/重试相同类型的异常

C# 捕获/修改(消息)/重试相同类型的异常,c#,exception,casting,throw,C#,Exception,Casting,Throw,我需要一个中心位置来从异常中提取信息,将我需要的所有信息设置为它的message参数,然后将该信息作为同一类型的异常重新播放 更好的解决方案可能是在最终处理异常(并记录其消息)的位置执行此操作,但是。。我可以控制引发异常的位置,而不是接收异常并只记录其消息内容的位置 除了这个设计决策,并且假设消息是一个只读属性,我必须(?)以某种方式创建一个新的异常对象,有没有办法使新的异常对象与原始对象的类型相同 这是我的代码,它没有编译-它在抛出行(我尝试动态地抛出对象)上绊倒了 是这样吗 throw (E

我需要一个中心位置来从异常中提取信息,将我需要的所有信息设置为它的message参数,然后将该信息作为同一类型的异常重新播放

更好的解决方案可能是在最终处理异常(并记录其消息)的位置执行此操作,但是。。我可以控制引发异常的位置,而不是接收异常并只记录其消息内容的位置

除了这个设计决策,并且假设消息是一个只读属性,我必须(?)以某种方式创建一个新的异常对象,有没有办法使新的异常对象与原始对象的类型相同

这是我的代码,它没有编译-它在抛出行(我尝试动态地抛出对象)上绊倒了

是这样吗

throw (Exception)newEx;
保留类型?(它进行编译。)


Convert.ChangeType是否确保获得正确类型的异常?

您可以这样做来动态调用异常类型的构造函数:

object newEx = Activator.CreateInstance(ex.GetType(), new object[] { msg });

您的原始代码在运行时会失败,因为对于
Convert.ChangeType
towork,异常类型必须实现
IConvertible
,并支持转换到其他异常类型,我对此表示怀疑 记住,Convert.ChangeType()将一种类型转换为另一种类型(假设存在这样的路径,例如将字符串转换为int)。大多数例外情况不会这样做(为什么会这样?)

为了实现这一点,您必须在运行时使用GetType()方法检查异常类型,并找到一个具有您可以满足的需求的构造函数并调用它。这里要小心,因为您无法控制如何定义所有异常,所以不能保证您可以访问“标准”构造函数

也就是说,如果你想打破规则,你可以这样做

void Main()
{
    try
    {   
        throw new Exception("Bar");
    }
    catch(Exception ex)
    {
        //I spit on the rules and change the message anyway
        ex.GetType().GetField("_message", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ex, "Foo");
        throw ex;
    }
}

可能有点晚了,但这对你有用吗

catch (Exception ex)
{
    throw new Exception("New message", ex);
}

您可以通过反射更改异常消息,如下所示

void Main()
{
    try
    {   
        throw new Exception("Bar");
    }
    catch(Exception ex)
    {
        //I spit on the rules and change the message anyway
        ex.GetType().GetField("_message", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ex, "Foo");
        throw ex;
    }
}
Exception Exception=新的异常(“某些消息”);
变量类型=类型(例外);
var flags=BindingFlags.Instance | BindingFlags.NonPublic;
var fieldInfo=type.GetField(“\u消息”,标志);
fieldInfo.SetValue(异常、消息);
所以你可以创建一个扩展方法

命名空间例外示例
{
公共静态类例外扩展
{
公共静态void SetMessage(此异常,字符串消息)
{
if(异常==null)
抛出新的ArgumentNullException(nameof(exception));
变量类型=类型(例外);
var flags=BindingFlags.Instance | BindingFlags.NonPublic;
var fieldInfo=type.GetField(“\u消息”,标志);
fieldInfo.SetValue(异常、消息);
}
}
}
然后用它

。。。
使用静态ExceptionExample.ExceptionExtensions;
公共类
{
公共方法()
{
var reader=AnotherClass.GetReader();
尝试
{
reader.Read();
}
捕获(例外情况除外)
{
var连接=读卡器?连接;
ex.SetMessage($”异常消息已被替换。\n\n原始消息:{ex.message}\n\n数据库:{connection?.Database}”);
throw;//不会丢失堆栈跟踪
}
}
}
您必须记住,如果使用“throw ex”,堆栈跟踪将丢失

要避免这种情况,您必须使用“throw;”,无例外

补充注释

这些都用于补充异常消息,但我发现使用“throw”并没有保留StackTrace——最后一个跟踪指向实际的“throw”语句(删除根本原因位置)

从其他地方的讨论中可以清楚地看到,由于CLR堆栈的限制,有些情况下throw无法保留


解决方案:在每个异常中转储StackTrace(例如,并添加到错误消息中)和/或转储到日志记录中

乍一看,您在这里试图针对消息执行的操作似乎已经通过
异常.ToString()方法完成了。如果生成并抛出这样的新异常(中断将发生在该方法中,而不是实际异常发生的位置),将丢失一定数量的上下文,并使调试更加困难。另外,我认为您可能需要一个泛型方法来生成正确的类型,但我可能错了……您是否使用更简单的
throw(Exception)newEx
遍历了调试器,以查看它是否保留了该类型?Exception.ToString非常有效。输出需要一点时间来适应,但除此之外,它似乎正是我想要的。(仅用于将来的编码,不用于手头的问题,也不用于将其分配给消息。)在此处为所有答案添加注释:对于一个糟糕的(设计)问题,获得一些真正有见地的答案,这是一个多么好的示例!!至少现在我确实知道如何在未来开始类似的事情。另外,我总是好奇地想了解一些关于反思的知识,我今天确实做到了。非常感谢你们所有人!您可能希望让它检查(或从
Activator.CreateInstance
捕获潜在异常),以确保特定异常类型实际实现了
(字符串消息)
构造函数。我知道很多(如果不是BCL中的全部,也不确定),但这不是保证。我只需要确保使用throw(Exception)newEx;,然后一切正常。Thx的洞察和想法。这对我来说很有效,即使使用“throw(Exception)newEx”,我的调用代码仍然能够捕获我需要的特定“ArgumentException”