抛出正确类型的例外

Sla*_*uma 10 .net c# exception

在我的代码中,我经常遇到这样的情况:

public void MyMethod(string data)
{
    AnotherClass objectOfAnotherClass = GetObject(data);
    if (objectOfAnotherClass == null)
        throw new WhatExceptionType1("objectOfAnotherClass is null.");

    if (objectOfAnotherClass.SomeProperty < 0)
        throw new WhatExceptionType2("SomeProperty must not be negative.");
}
Run Code Online (Sandbox Code Playgroud)

想象一下,GetObject使用一些不受我控制的外部库,null如果没有对象data存在则返回该库,并将负数SomeProperty视为有效状态,因此不会抛出异常.进一步设想,MyMethod如果不工作objectOfAnotherClass,并没有什么意义了负SomeProperty.

WhatExceptionType1/2投入这种情况的正常例外是什么?

基本上我有四个选择:

  • 1)InvalidOperationException,因为MyMethod在上述条件下没有意义.另一方面,指南(以及VS中的Intellisense)表示如果方法所属的对象处于无效状态,则应抛出InvalidOperationException.现在对象本身不处于无效状态.相反,输入参数data和基于该参数的一些其他操作导致MyMethod不能再操作的情况.

  • 2)ArgumentException,因为有方法的值data可以使用和方法不能的其他值.但我无法通过data单独检查来检查这一点,在决定之前我必须调用其他操作.

  • 3)Exception,因为我不知道使用哪个其他异常类型,并且因为所有其他预定义异常都觉得太专业并且不适合我的情况.

  • 4)MyCustomException(我自己的异常类型派生自Exception).这似乎总是一个选项,但我担心当我开始遵循这种模式时,我必须为许多不同的错误条件定义许多特殊的异常类.

还有其他更好的选择吗?支持或反对这些选择的论据是什么?

感谢您提前的反馈!

ash*_*999 8

如果存在有意义的内置异常,我会使用它们.如果没有,那么滚动自己的异常是有意义的 - 即使它是一个扩展Exception的空类 - 因为这允许您检测特定的异常类型.例如,如果你只是抛出异常,你怎么知道异常是因为objectOfAnotherClass是null,并且它不是引发了一些异常GetObject

总而言之:具体的异常更好,因为您可以(可能)诊断并从特定情况中恢复.因此,使用内置的.NET异常(如果它们足够),或者滚动自己的异常.

编辑:我应该澄清,我很少使用现有的异常并在其中添加消息.如果异常类型告诉您错误,而不是必须调试,生成异常,然后检查消息以查看问题是什么,它会使您的代码更具可读性.

  • 但是需要创建特定的异常(正确:你的Serializable吗?),并进行维护,如果他们所做的只是"使你的代码更具可读性",那么你应该不这样做. (2认同)
  • @ashes:捕获特定类型的原因是因为当捕获该类型时,您的代码将执行不同的操作.如果你的代码对于'Exception`的行为和`SpecificException`的行为相同,那么请改用`Exception`. (2认同)
  • Slauma>使您的自定义异常尽可能通用,以便可以将它们重用于其他方案.EG`InvalidObjectException` (2认同)

Jam*_*ing 5

使用内置异常类型时要小心......它们对.NET框架有非常具体的含义,除非你使用它具有完全相同的含义,否则最好自己滚动(+1到John Saunders Serializeable).

InvalidOperationException 有意思:

方法调用对于对象的当前状态无效时引发的异常.

例如,如果您调用SqlConnection.Open(),InvalidOperationException则在未指定数据源的情况下获得. InvalidOperationException不适合您的方案.

ArgumentException也不合适.创建失败objectOfAnotherClass可能与传入的错误数据无关.假设它是文件名,但GetObject()没有读取文件的权限.在编写方法时,无法知道调用GetObject()失败的原因,并且可以说最好的是返回的对象为null或无效.

Exception通常只是一个坏主意......它让调用者完全不知道为什么该方法无法创建对象.(就此而言,只有一个catch (Exception ex) {..}也是一个坏主意)

您需要能够清楚地识别出错的异常,这意味着要创建自己的异常.尽量保持通用,以避免1,000个自定义异常.我建议:

ObjectCreateException:   // The call to GetObject() returned null<br />
InvalidObjectException:  // The object returned by GetObject() is invalid 
                         // (because the property < 0)
Run Code Online (Sandbox Code Playgroud)

感谢投票〜我想我会添加一些我们写过的自定义异常的例子.

请注意,您实际上不需要向方法添加任何代码,因为自定义异常实际上并没有做出与其基类不同的任何操作.他们只代表不同的东西.第二个例子确实为异常添加了一个属性,因此有代码支持它(包括构造函数).

第一个是我们所有例外的通用基础; 规则"不要捕获一般异常"适用(尽管我们仍然这样做......它允许我们区分我们生成的异常和框架抛出的异常).第二个是一个更具体的异常,它源自Gs3Exception并序列化自定义属性.

.NET开发团队决定ApplicationException没有现实世界的价值并且不赞成它,但我的纯粹主义者总是喜欢它,所以它仍然存在于我的代码中.但是,在这里,它确实没有增加任何价值,只增加了继承层次的深度.所以可以直接从中继承Exception.

/// <summary>
/// A general, base error for GS3 applications </summary>
[Serializable]
public class Gs3Exception : ApplicationException {


    /// <summary>
    /// Initializes a new instance of the <see cref="Gs3Exception"/> class </summary>
    public Gs3Exception() {}


    /// <summary>
    /// Initializes a new instance of the <see cref="Gs3Exception"/> class </summary>
    /// <param name="message">A brief, descriptive message about the error </param>
    public Gs3Exception(string message) : base(message) {}


    /// <summary>
    /// Initializes a new instance of the <see cref="Gs3Exception"/> class 
    /// when deserializing </summary>
    /// <param name="info">The object that holds the serialized object data </param>
    /// <param name="context">The contextual information about the source or
    ///  destination.</param>
    public Gs3Exception(SerializationInfo info, StreamingContext context) : base(info, context) { }


    /// <summary>
    /// Initializes a new instance of the <see cref="Gs3Exception"/> class
    /// with a message and inner exception </summary>
    /// <param name="Message">A brief, descriptive message about the error </param>
    /// <param name="exc">The exception that triggered the failure </param>
    public Gs3Exception(string Message, Exception exc) : base(Message, exc) { }


}


/// <summary>
/// An object queried in an request was not found </summary>
[Serializable]
public class ObjectNotFoundException : Gs3Application {

    private string objectName = string.Empty;

    /// <summary>
    /// Initializes a new instance of the <see cref="ObjectNotFoundException"/> class </summary>
    public ObjectNotFoundException() {}


    /// <summary>
    /// Initializes a new instance of the <see cref="ObjectNotFoundException"/> class </summary>
    /// <param name="message">A brief, descriptive message about the error</param>
    public ObjectNotFoundException(string message) : base(message) {}


    /// <summary>
    /// Initializes a new instance of the <see cref="ObjectNotFoundException"/> class </summary>
    /// <param name="ObjectName">Name of the object not found </param>
    /// <param name="message">A brief, descriptive message about the error </param>
    public ObjectNotFoundException(string ObjectName, string message) : this(message) {
        this.ObjectName = ObjectName;
    }


    /// <summary>
    /// Initializes a new instance of the <see cref="ObjectNotFoundException"/> class.
    /// This method is used during deserialization to retrieve properties from 
    /// the serialized data. </summary>
    /// <param name="info">The object that holds the serialized object data.</param>
    /// <param name="context">The contextual information about the source or
    /// destination.</param>
    public ObjectNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) {
        if (null != info) {
            this.objectName = info.GetString("objectName");
        }
    }


    /// <summary>
    /// When serializing, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> 
    /// with information about the exception. </summary>
    /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds 
    /// the serialized object data about the exception being thrown.</param>
    /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// The <paramref name="info"/> parameter is a null reference (Nothing in Visual Basic) </exception>
    /// <PermissionSet>
    ///     <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
    ///     <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
    /// </PermissionSet>
    [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {

        base.GetObjectData(info, context);

        //  'info' guaranteed to be non-null (base.GetObjectData() will throw an ArugmentNullException if it is)
        info.AddValue("objectName", this.objectName);

    }


    /// <summary>
    /// Gets or sets the name of the object not found </summary>
    /// <value>The name of the object </value>
    public string ObjectName {
        get { return objectName; }
        set { objectName = value; }
    }

}
Run Code Online (Sandbox Code Playgroud)

PS:在任何人打电话给我之前,基础Gs3Exception没有增加价值的原因ApplicationException是企业库异常处理应用程序块...通过具有应用程序级基本异常,我们可以为抛出的异常创建一般日志记录策略直接通过我们的代码.