C# 为什么要用指定的TExceptionArgs定义自定义异常?
我正在通过C#书阅读CLR,作者展示了如何将自定义异常定义为:C# 为什么要用指定的TExceptionArgs定义自定义异常?,c#,.net,C#,.net,我正在通过C#书阅读CLR,作者展示了如何将自定义异常定义为: class Program { static void Main(string[] args) { TestException(); } static void TestException() { try { throw new Exception<DiskFullExceptionArgs>(new DiskFullExceptionArgs(@"
class Program {
static void Main(string[] args) {
TestException();
}
static void TestException() {
try {
throw new Exception<DiskFullExceptionArgs>(new DiskFullExceptionArgs(@"C:\"), "The disk is full");
}
catch (Exception<DiskFullExceptionArgs> e) {
Console.WriteLine(e.Message);
}
}
}
[Serializable]
public sealed class DiskFullExceptionArgs : ExceptionArgs {
private readonly String m_diskpath; // private field set at construction time
public DiskFullExceptionArgs(String diskpath) {
m_diskpath = diskpath;
}
public String DiskPath { get { return m_diskpath; } }
// Override the Message property to include our field (if set)
public override String Message {
get {
return (m_diskpath == null) ? base.Message : "DiskPath=" + m_diskpath;
}
}
}
[Serializable]
public abstract class ExceptionArgs {
public virtual String Message { get { return String.Empty; } }
}
[Serializable]
public sealed class Exception<TExceptionArgs> : Exception, ISerializable where TExceptionArgs : ExceptionArgs {
private const String c_args = "Args"; // For (de)serialization
private readonly TExceptionArgs m_args;
public TExceptionArgs Args { get { return m_args; } }
public Exception(String message = null, Exception innerException = null) : this(null, message, innerException) {
}
public Exception(TExceptionArgs args, String message = null, Exception innerException = null) : base(message, innerException) {
m_args = args;
}
// This constructor is for deserialization; since the class is sealed, the constructor is
// private. If this class were not sealed, this constructor should be protected
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
private Exception(SerializationInfo info, StreamingContext context) : base(info, context) {
m_args = (TExceptionArgs)info.GetValue(c_args, typeof(TExceptionArgs));
}
// This method is for serialization; it’s public because of the ISerializable interface
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public override void GetObjectData(SerializationInfo info, StreamingContext context) {
info.AddValue(c_args, m_args);
base.GetObjectData(info, context);
}
public override String Message {
get {
String baseMsg = base.Message;
return (m_args == null) ? baseMsg : baseMsg + " (" + m_args.Message + ")";
}
}
public override Boolean Equals(Object obj) {
Exception<TExceptionArgs> other = obj as Exception<TExceptionArgs>;
if (other == null)
return false;
return Object.Equals(m_args, other.m_args) && base.Equals(obj);
}
public override int GetHashCode() { return base.GetHashCode(); }
}
类程序{
静态void Main(字符串[]参数){
TestException();
}
静态无效测试异常(){
试一试{
抛出新异常(newdiskFullExceptionArgs(@“C:\”),“磁盘已满”);
}
捕获(例外e){
控制台写入线(e.Message);
}
}
}
[可序列化]
公共密封类DiskFullExceptionArgs:ExceptionArgs{
私有只读字符串m_diskpath;//在构造时设置私有字段
public DiskFullExceptionArgs(字符串diskpath){
m_diskpath=diskpath;
}
公共字符串DiskPath{get{return m_DiskPath;}}
//覆盖消息属性以包括我们的字段(如果设置)
公共覆盖字符串消息{
得到{
返回(m_diskpath==null)?base。消息:“diskpath=“+m_diskpath;
}
}
}
[可序列化]
公共抽象类例外args{
公共虚拟字符串消息{get{return String.Empty;}}
}
[可序列化]
公共密封类异常:异常,可序列化,其中TExceptionArgs:ExceptionArgs{
私有常量字符串c_args=“args”//用于(反)序列化
私有只读TExceptionArgs m_args;
公共TExceptionArgs Args{get{return m_Args;}}
公共异常(字符串message=null,异常innerException=null):此(null,message,innerException){
}
公共异常(TExceptionArgs args,字符串message=null,异常innerException=null):基本(消息,innerException){
m_args=args;
}
//此构造函数用于反序列化;因为类是密封的,所以构造函数是
//如果这个类没有被密封,这个构造函数应该受到保护
[SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.SerializationFormatter)]
私有异常(SerializationInfo,StreamingContext上下文):基本(信息,上下文){
m_args=(TExceptionArgs)info.GetValue(c_args,typeof(TExceptionArgs));
}
//此方法用于序列化;由于ISerializable接口,它是公共的
[SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.SerializationFormatter)]
公共覆盖无效GetObjectData(SerializationInfo、StreamingContext上下文){
信息添加值(c_参数、m_参数);
base.GetObjectData(信息、上下文);
}
公共覆盖字符串消息{
得到{
字符串baseMsg=base.Message;
返回(m_args==null)?baseMsg:baseMsg+“(“+m_args.Message+”);
}
}
公共覆盖布尔等于(对象obj){
异常其他=作为异常的obj;
如果(其他==null)
返回false;
返回Object.Equals(m_参数,其他.m_参数)和&base.Equals(obj);
}
公共重写int GetHashCode(){return base.GetHashCode();}
}
我有两个问题:
Q1定义自定义异常argsExceptionArgs
并创建具体的DiskFullExceptionArgs
有什么好处?
根据我的理解,您可以将整个消息“磁盘已满C:”传递给自定义异常,为什么将“C:”分隔为ExceptionArgs
和“磁盘已满”传递给自定义异常
Q2-此自定义异常覆盖
等于方法。但为什么需要比较两个异常实例呢?我没有看到任何需要比较两个异常实例的场景,因为当程序运行时出现错误时,可以使用自定义异常向异常添加清晰、有意义且用户友好的信息。当内置异常类型不能满足您的需要时,它们非常有用。
请参考此处创建一个Q1:“考虑提供异常属性,以便通过编程访问与异常相关的额外信息(除了消息字符串之外)。”
自定义属性允许使用比消息更复杂的属性。您可能希望捕获一些HttpContext,例如,URL、referer、会话Id等等
问题2:也许您希望在某个日志中只报告一次每个异常。如果你有自定义属性,你可能也想比较它们
但也可以查看所有异常的.Data
属性,并查看建议“考虑抛出驻留在系统名称空间中的现有异常,而不是创建自定义异常类型。”您可能根本不需要自定义异常来向异常日志记录系统传递额外属性:使用现有异常并在.Data
中传递属性。您指的是哪本书以及作者是谁?不幸的是,有很多书籍、文章和教程中都有非权威作者编写的不好或过时的建议。OP不是询问自定义异常子类,而是询问在异常类型中使用泛型来承载args对象。