views:

1224

answers:

7

I have a string property that has a maximum length requirement because the data is linked to a database. What exception should I throw if the caller tries to set a string exceeding this length?

For example, this C# code:

public string MyProperty
{
    get
    {
        return _MyBackingField;
    }
    set
    {
        if (value.Length > 100)
            throw new FooException("MyProperty has a maximum length of 100.");

        _MyBackingField = value;
    }
}

I considered ArgumentException, but it just doesn't seem right. Technically, it is a function - MyProperty_set(string value) - so a case for ArgumentException can be made, but it's not being called as a function to the consumer's eyes - it's on the right side of an assignment operator.

This question could probably also be extended to include all kinds of data validation done in property setters, but I'm particularly interested in the above case.

A: 

You may use InvalidOperationException. That's a compromise. I wouldn't bother using an ArgumentException either.

Sascha
+16  A: 

Have a look through mscorlib.dll with Reflector, in a similar situation such as System.String.StringBuilder.Capacity Microsoft use ArgumentOutOfRangeException() similar to:

public int PropertyA
{
    get
    {
        return //etc...
    }
    set
    {
        if (condition == true)
        {
            throw new ArgumentOutOfRangeException(/* etc... */);
        }
        // ... etc
    }
}
Richard Slater
i believe you can't publish code straight from Microsoft libraries...
Orentet
Ooops - have re-written my example, do you think I would get away with that?
Richard Slater
Btw, Mono throws an ArgumentException in this case...
Christoph Rüegg
"ArgumentOutOfRangeException - The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method".... imo, Mono should fix their code.
Peter Lillevold
+7  A: 

To me ArgumentException (or a child) makes more sense, because the argument (value) you provided is not valid, and this is what ArgumentException was created for.

Davide Vosti
+5  A: 

I wouldn't throw an exception at all. Rather I would allow a string of any length and then have a seperate "Validate" method on the class that is called before saving. There are a number of scenarios particularly if you use databinding where throwing exceptions from property setters can get you in a mess.

The trouble with throwing exceptions from property setters is that programmers forget to catch them. It kind of depends upon how clean you expect the data you are getting to be. In this case I would expect long string lengths to be common not exceptional and as such using an exception would be "flow control with exceptions".

To quote from the Microsoft's Design Guidelines for Developing Class Libraries:

Do not use exceptions for normal flow of control, if possible. Except for system failures and operations with potential race conditions, framework designers should design APIs so that users can write code that does not throw exceptions. For example, you can provide a way to check preconditions before calling a member so that users can write code that does not throw exceptions.

Martin Brown
While I personally do not agree at all (Validation is the job of the setter after all - otherwise there is no point in having anything else than simple field = value; return field; getters/setters - I like that you raise the point of DataBinding which indeed can get you in a mess.
Michael Stum
Exception throwing in property setters is OK to do, I would not throw exceptions in property getters though.
Patrick Peters
Delaying the throwing of a exception won't allow you to catch the infractor. Also, if a programmer forgets to catch a exception then it will jump on his face instead of bubbling up inadvertently.
Trap
@Trap: I suppose this depends upon what you are designing the class for. In the case where you expect to DataBind to objects of the class, you would not want it to blow up when the user types in one to many characters. Implementing System.ComponentModel.IDataErrorInfo is the standard way arround this. In the setter instead of throwing an exception you set an internal flag called something like IsMyPropertyToLong. Later you use this to return the correct error message from this[string]
Martin Brown
Glad to see the excerpt from the design guidelines. I like the idea that the setter throws, and the error is immediately apparent. In regards to writing code that does not throw an exception I like the TryParse pattern or in this case bool TrySet(value) which would not throw.
David Silva Smith
+2  A: 

Remember how many problems in computer science are solved by adding an extra level of indirection?

One approach would be to create a new type, FixedLengthString, say. It would be instances of that type that validate lengths of strings they are initialised with - with a conversion operator to do type conversion from a plain string. If your property setter takes such a type as its argument then any violation would become a type conversion exception instead of an argument/ property exception.

In practice I would rarely do this. It smells a bit of taking OO too far - but in some cases it can be a useful technique, so I mention it here for completeness.

Phil Nash
A: 

Try to use existing exceptions whereever possible. In this case use InvalidOperationException because the incoming value is bringing the object in an inconsistent state. Custom exceptions can be created when a specific handling with the custom exception is needed. In this case you only throw an exception with some text, so use the InvalidOperationException.

When throwing the InvalidOperationException show the value that has been passed to this setter.

Patrick Peters
Of course you can have the best of both worlds and create your own exception type which derives from, say, InvalidOperationException. Of course you still pay a little in extra code and code complexity. It's a small cost but after weighing it you may still consider it not worth it
Phil Nash
InvalidOperationException shouldn't be raised in this case. What is wrong is the value, not the operation of setting the value.
Trap
+2  A: 
public IPAddress Address
{
    get
    {
        return address;
    }
    set
    {
        if(value == null)
        {
            throw new ArgumentNullException("value");
        }
        address = value;
    }
}

via MSDN

javros
Good point, although this is specific for the null case. However, it does support the use for the `ArgumentException` series, so +1 for the reference.
lc