views:

410

answers:

5

In C# I want to communicate to the calling method that the parameters passed to an object have caused its instantiation to fail.

// okay
Banana banana1 = new Banana("green");

// fail
Banana banana2 = new Banana("red");

Throw an exception? If so how?

+18  A: 
throw new ArgumentException("Reason", "param name");
Cody Brocious
+1 - just remember that it may be more appropriate to throw one the derived exceptions such as ArgumentNullException or ArgumentOutOfRangeException, or a similarly purposed exception such as FormatException.
Greg Beech
A: 

I think you want ArgumentException...

Jason Punyon
+4  A: 

The most accepted solution is to throw an exception. To prove this, open reflector and have a look at most of the classes from the BCL and the exceptions that they can throw on construction.

As an example. List(IEnumerable collection) will throw an exception if collection is null. A perfectly valid way of communicating errors to the caller.

Ray Booysen
"The most accepted solution is to throw an exception", I'd even question if any other strategy makes sense.
John MacIntyre
+1  A: 

If your constructor fails, you should throw an exception. By defenition, Constructors do not return values, so you cant return, say, an error code.

public class Banana 
{
    public Banana(string color)
    {
        if ( color == "red" )
          throw new SomeException("intialization failed, red is an invalid color");

    }
}
//////////////////////////////////////////////////////////     
try
{
    Banana banana1 = new Banana("green");
    banana1.whatever();
}
catch( SomeException error )
{
    // do something
}
finally
{
    // always do  this stuff
}
Muad'Dib
The BCL includes ArgumentException for exactly this. Unless there's a damn good reason, you should stick to the base exceptions, as that's what people are used to.
Cody Brocious
+7  A: 

A lot (all?) of the answers are saying to throw an exception, but I'm sure I've seen official statements from the framework design team advising against throwing exceptions from constructors.

Note that the classes in the .NET framework that behave similarly to your "Banana" example (where only certain values are appropriate to instantiate the object with) don't use constructors, but instead use static factory methods. For example, System.Net.WebRequest has no public constructor, and instead uses a static Create method which can raise an exception if the supplied string is not a valid URI. With some exceptions - see my update below.

So for your code I would change Banana's constructor to protected, and introduce a method like this:

public static Banana Create(string color) 
{
    if (color != "green" && color != "yellow")
    {
        throw new ArgumentException("Color must be 'green' or 'yellow'", 
            "color");
    }
    return new Banana(color);
}

Update

Ok, it seems like it's not a bad idea to throw exceptions from a constructor. In fact, System.IO.FileStream does just that if you pass an invalid filename to its constructor. I guess the idea of using a static factory method is just one way of being more explicit about how you're creating the instance (for example, if the method above were called "FromColor").

Matt Hamilton
It is not a bad idea to throw an exception from a constructor. There is no possible reason or justification for that viewpoint. If it were a guideline there would be an FxCop rule for it, and there isn't.
Greg Beech
It uses a static method because WebRequest is abstract. It uses the parameter to decide whether to create an HttpWebRequest or a FileWebRequest.
Rob Kennedy
You're probably thinking of a *static* constructor from which it isn't a good idea to throw an exception as static constructors are not retriable and it makes the type unusable. However this is not a problem with *instance* constructors.
Greg Beech
Ah WebRequest might have been a bad example then. I'm sure I've seen other examples for just this reason. Is there a class in the BCL that might throw an exception from its ctor?
Matt Hamilton
FileStream! It throws an ArgumentException in its constructor if you pass in an invalid path. I will amend my answer accordingly.
Matt Hamilton
@Matt-Even in your example, if the Banana() constructor had an error, shouldn't that throw an exception to your static Create() classfactory method?
John MacIntyre
I dunno, John. I would normally code a ctor such that it couldn't throw an error. Guard against it in other ways. The "banana/colour" example is pretty contrived - in that case I'd have a BananaColor enum and pass that in so only valid values are possible.
Matt Hamilton
You could still have invalid values with an enum, e.g. enum BananaColor { Yellow, Green }; BananaColor invalid = (BananaColor)123; Enums aren't properly type-safe so you still need to check for invalid values and throw.
Greg Beech
But, Matt, like you said, this example is contrived. But if inside your constructor, there was an error, for what ever reason. The only other way I can see to communicate that error might be an 'IsValid' property on the object ... which I don't think is as elegant as an exception (IMHO).
John MacIntyre
BTW- +1 for your openness to discuss this, and keep the comment conversation going.
John MacIntyre
Oh yeah I didn't want to delete the question even if it's not correct. I still think it brings another viewpoint to the conversation. I like factory methods for this sort of thing, but this post has "brought me around" to exceptions from ctors.
Matt Hamilton