C# 为什么要用指定的TExceptionArgs定义自定义异常?

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(@"

我正在通过C#书阅读CLR,作者展示了如何将自定义异常定义为:

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定义自定义异常args
ExceptionArgs
并创建具体的
DiskFullExceptionArgs
有什么好处? 根据我的理解,您可以将整个消息“磁盘已满C:”传递给自定义异常,为什么将“C:”分隔为
ExceptionArgs
和“磁盘已满”传递给自定义异常


Q2-此自定义异常覆盖
等于
方法。但为什么需要比较两个异常实例呢?我没有看到任何需要比较两个异常实例的场景,因为当程序运行时出现错误时,可以使用自定义异常向异常添加清晰、有意义且用户友好的信息。当内置异常类型不能满足您的需要时,它们非常有用。
请参考此处创建一个

Q1:“考虑提供异常属性,以便通过编程访问与异常相关的额外信息(除了消息字符串之外)。”

自定义属性允许使用比消息更复杂的属性。您可能希望捕获一些HttpContext,例如,URL、referer、会话Id等等

问题2:也许您希望在某个日志中只报告一次每个异常。如果你有自定义属性,你可能也想比较它们


但也可以查看所有异常的
.Data
属性,并查看建议“考虑抛出驻留在系统名称空间中的现有异常,而不是创建自定义异常类型。”您可能根本不需要自定义异常来向异常日志记录系统传递额外属性:使用现有异常并在
.Data

中传递属性。您指的是哪本书以及作者是谁?不幸的是,有很多书籍、文章和教程中都有非权威作者编写的不好或过时的建议。OP不是询问自定义异常子类,而是询问在异常类型中使用泛型来承载args对象。