简体   繁体   English

应该在哪里抛出异常?

[英]Where should exceptions been thrown?

I have a class that looks the following way: 我有一堂课,看起来像下面这样:

public class StackOverflowQuestion {

    private string _question;

    public string Question {
        get { return _question;  }
        set { _question = value; }
    }

    public StackOverflowQuestion(string question) {
        _question = question;
    }

    public override string ToString() {
        return _question;
    }
}

Now, the value "question" isn't allowed to be null or empty and the user should be notified via a ArgumentNullException - but where should it been thrown? 现在,不允许将值“ question”设置为null或为空,并且应通过ArgumentNullException通知用户-但应将其扔到哪里? According to the 'fail-fast' principle -> Everywhere. 根据“快速失败”原则->无处不在。

public class StackOverflowQuestion {

    private string _question;

    public string Question {
        get { return _question;  }
        set { 
           if(!String.IsNullOrEmpty(value))
                _question = value
            else throw new ArgumentNullException("value");
        }
    }

    public StackOverflowQuestion(string question) {
        if(!String.IsNullOrEmpty(question))
            _question = question;
        else throw new ArgumentNullException("question");
    }

    public override string ToString() {
        if(!String.IsNullOrEmpty(_question)) return _question;
        else throw new ArgumentNullException("_question");
    }
}

Now this is obviously ridiculous and extremely repetitive. 现在,这显然是荒谬的,并且是非常重复的。 But it seems right: If the value is set through .ctor, it fails directly after a short check. 但这似乎是正确的:如果通过.ctor设置了该值,则经过短暂检查后,该值将立即失败。 When its set through the property, it fails directly after a short check.. but who expects exceptions on a setter? 当通过属性设置它时,它会在短暂检查后直接失败..但是谁会期望setter发生异常? And when I output the string, I expect a string, not an exception for something that should have happend long ago, but again: If it's wrong, it should fail ASAP, even if 'soon' is quite late. 当我输出字符串时,我期望的是一个字符串,不是很早就应该发生的异常,而是再次出现:如果错了,即使'soon'已经很晚了,它也应该尽快失败。

So, where should the only exception handling been done? 那么,唯一的异常处理应该在哪里完成呢? Am I asking for a 'best-practice', or is this a taste thing? 我是否要求“最佳做法”,或者这是有味道的事情?

Since _question is private, there's no need to check whether it is null in ToString() (unless you're just sanity checking your own code). 由于_question是私有的,因此不需要在ToString()中检查它是否为null(除非您只是在理智地检查自己的代码)。

You can avoid the check in the constructor by having the constructor use the property setter. 您可以通过使构造函数使用属性设置器来避免在构造函数中进行检查。 Thus, I'd recommend: 因此,我建议:

public class StackOverflowQuestion {

    private string _question;

    public string Question {
        get { return _question;  }
        set { 
           if(string.IsNullOrEmpty(value))
                // to make this more transparent when thrown through the constructor, it might
                // be preferable to throw a real error message like "Question: cannot be null or empty"
                throw new ArgumentException("value");
           this._question = value;
        }
    }

    public StackOverflowQuestion(string question) {
        this.Question = question;
    }

    public override string ToString() {
        return this.Question;
    }
}

A few things to note: 1. You should throw ArgumentException rather than ArgumentNullException for empty strings (if you want you can do 2 checks and still throw ArgumentNullException for nulls). 需要注意的几件事:1.对于空字符串,应该抛出ArgumentException而不是ArgumentNullException (如果您愿意,可以进行2次检查,并且仍然将ArgumentNullException抛出为null)。 2. While the approach uses less code, the one disadvantage is that the error message users get is slightly worse than when they pass null to the constructor, since the failure happens 2 levels deep instead of one. 2.虽然该方法使用的代码更少,但一个缺点是,用户收到的错误消息比将null传递给构造函数时要差一些,因为失败发生的深度是2级而不是1级。

You only need to test it once - in the single place where you set the variable, having changed the constructor to use the property: 您只需对它进行一次测试-在设置变量的单个位置,将构造函数更改为使用该属性:

public class StackOverflowQuestion
{
    private string _question;

    public string Question
    {
        get { return _question; }
        set
        { 
           if (String.IsNullOrEmpty(value))
           {
               throw new ArgumentException("Question cannot be null or empty",
                                           "value");
           }
           _question = value;
        }
    }

    public StackOverflowQuestion(string question)
    {
        Question = question;
    }

    public override string ToString()
    {
        return Question;
    }
}

The one downside here is that the "bad parameter" name will be value rather than question when it's null in the constructor, but I think that's a price worth paying. 这里的一个缺点是“坏参数”名称将是value而不是在构造函数中为空时的question ,但我认为这是值得付出的代价。 An alternative is just to use the message, and not specify the parameter name. 一种替代方法是使用消息,而不指定参数名称。

You may want to separate out null from empty , so that you can throw an ArgumentNullException when it's null - but you shouldn't be throwing ArgumentNullException when it's just empty. 可能希望将nullempty分开,以便可以在ArgumentNullException为null时抛出它-但当它为空时,则不应抛出ArgumentNullException

You don't need to perform any checks when fetching the value, as you know it will never be null or empty, because you're preventing it from ever being set that way. 获取值时,您无需执行任何检查,因为您知道它永远不会为null或为空,因为您正在阻止以这种方式进行设置。

You should also consider whether you could make the class immutable, at which point you'd only need to test in the constructor as there wouldn't be a setter... 您还应该考虑是否可以使该类不可变,这时您只需要在构造函数中进行测试,因为不会有设置器。

I would rather make it immutable: 我宁愿使其不变:

public class StackOverflowQuestion
    {
        public string Question
        {
            get; private set;
        }

        public StackOverflowQuestion(string question)
        {
            if (String.IsNullOrEmpty(question))                
              throw new ArgumentNullException("question");

            Question = question;
        }

        public override string ToString()
        {
            return Question;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在哪里处理Task抛出的异常 - Where to handle exceptions thrown by Task 在哪里可以找到有关类和方法抛出的异常的信息? - Where to find information on exceptions thrown by classes and methods? Visual Studio 2010错误:“调用目标已抛出异常” - Visual Studio 2010 error: “Exceptions has been thrown by the target of an invocation” Rx中的ArgumentException。 应该扔到哪里? - ArgumentException in Rx. Where should be thrown? .NET中的无效或意外参数应该抛出哪些异常? - What exceptions should be thrown for invalid or unexpected parameters in .NET? ChannelFactory引发的WCF异常 - WCF Exceptions thrown by ChannelFactory 我应该如何处理WCF服务中引发的异常? - How should I go about handling exceptions thrown within a WCF service? 我应该如何处理在使我变得同步的异步方法期间引发的异常? - How should I handle exceptions that are thrown during asynchronous methods that I've made syncrhonous? 我应该如何处理从WCF回调接口对象集合之一引发的异常? - How should I handle exceptions thrown from one of a collection of WCF callback interface objects? 在BLL,DAL或PL中,我应该在哪里处理异常? - Where should I handle the exceptions, in the BLL, DAL or PL?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM