Atrotygma
Atrotygma

Reputation: 1153

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? 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. When its set through the property, it fails directly after a short check.. but who expects exceptions on a 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.

So, where should the only exception handling been done? Am I asking for a 'best-practice', or is this a taste thing?

Upvotes: 1

Views: 100

Answers (3)

Vladimir Gondarev
Vladimir Gondarev

Reputation: 1243

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;
        }
    }

Upvotes: 1

ChaseMedallion
ChaseMedallion

Reputation: 21764

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).

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). 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.

Upvotes: 3

Jon Skeet
Jon Skeet

Reputation: 1500465

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. 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.

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.

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...

Upvotes: 2

Related Questions