Quick rant: why isn’t there an Exception(string, params object[]) constructor?

This Stack Overflow question has reminded me of something I often wish existed in common exception constructors – an overload taking a format string and values. For instance, it would be really nice to be able to write:

throw new IOException(“Expected to read {0} bytes but only {1} were available”,
                      requiredSize, bytesRead);

Of course, with no way of explicitly inheriting constructors (which I almost always want for exceptions, and almost never want for anything else) it would mean yet another overload to copy and paste from another exception, but the times when I’ve actually written it in my own exceptions it’s been hugely handy, particularly for tricky cases where you’ve got a lot of data to include in the message. (You’d also want an overload taking a nested exception first as well, adding to the baggage…)

24 thoughts on “Quick rant: why isn’t there an Exception(string, params object[]) constructor?”

  1. This is interesting – pretty much the single thing I liked about Python, they recently removed, and that was the % operator, which says “to the left of this is a format string, and to the right is a bunch of values to put into it”. If C# had some shorthand for System.String.Format, that would make it easier.

    Since you use Resharper like me, a good workaround is to just type the format string you imagine, invoke the lightbulb and choose “surround by string.Format” (it picks up the placeholders, fills in the string.Format call around the caret and moves you to the position in which you can start writing arguments).

  2. Given the existence of String.Format, I don’t really want a bunch of overloaded methods scattered around the framework which do exactly the same thing. Should any method anywhere which takes a string also have a format overload? Why not just use the method we already have?

  3. @David: So do you always write Console.WriteLine(string.Format(“….”, x, y, z)? It’s a convenience – and one which adds significantly to readability IMO.

    @cb: I’m not keen on method names like “F” personally. In particular, anyone coming to the code and unfamiliar with your method will be confused for a bit – I suspect they would easily understand the presence of a “format string and varargs” constructor because it’s so similar to Console.WriteLine.

  4. I think this is an instance of a case I’ve been thinking about recently: where do you specialize in the code? For example, do you have a GetAllProducts() method that you specialize at the place of the call with a .Where(p => p.Supplier == “Microsoft”) or do you write a GetProductsForSupplier(“Microsoft”) method?

    So far I’m thinking that low level layers (like the data layer) should generalize, while high level layers (like the controller) should specialize… a /ProductsFrom/Microsoft action makes more sense than a /Products?Supplier=Microsoft one. I’d love to read more on this subject.

  5. I don’t really see the big deal here. I have to agree with David, an additional String.Format call really doesn’t seem that onerous.

    Moreover, you should put your throw statements into a separate static ThrowHelper class anyway for performance reasons, and so you might as well use that class to define your overloads with formatting parameters.

  6. I think Jesper’s has a point

    Formatting strings is a very common need, so common that shortcuts were sometimes implemented (Console.WriteLine…)

    Do you remember the time where we had to implement iterators the hard way (prior to yield return)?
    The C# team was smart enough to realize that yield was a very handy tool and decided to include it in the language. Same could apply here

  7. @Chris: Could you explain your reasoning for the “ThrowHelper” class? It sounds like another level of indirection for no good reason, unless it makes a *huge* performance gain. What kind of thing is in there, and what difference does it make?

  8. Jon, the fundamental issue is that a throw statement translates into a surprisingly large amount of machine code. Checking the JITter output for both platforms (32/64 bit) and compiler modes (debug/release) on .NET 3.5 SP1, I counted between 30 and 60 extra bytes compared to an equivalent ThrowHelper call.

    For one thing, that’s going to bloat your code image significantly if you carefully guard all your arguments etc., as the entire rest of a short method is often shorter than the output for a single throw.

    For another thing, the extra size is going to prevent inlining of short methods. I quickly found a test case for getter-sized methods where a ThrowHelper variant would get inlined but the throw variant would not.

    The 2nd ed. of the “Framework Design Guidelines” also recommends using exception builder methods for this reason (page 220f), although they incorrectly claim that throw code never gets inlined (it does, it’s just often too big). And if you look through the BCL with Reflector you’ll find that Microsoft used ThrowHelper calls all over the place, in preference of throw statements.

    Also, I hear some people want string formatting with their exceptions, and a ThrowHelper class is a convenient place for that functionality. :)

    Another thought regarding String.Format: you should generally specify an explicit IFormatProvider (usually either CurrentCulture or InvariantCulture). What should the implicit formatting culture be for that hypothetical formatting constructor provided by the exception? Wouldn’t you need yet another ctor that also takes an IFormatProvider? That’s another argument for custom ThrowHelper methods, since you just specify the desired IFormatProvider in one place.

  9. With the added benefit of formatting (and anything else useful, such as nullity checking etc) I can see an argument for using a ThrowHelper all over the place. The performance argument doesn’t sway me for anything other than performance-critical code, where micro-optimisation is more reasonable. It’s nice to know, for the sake of those few places where performance really *is* that important, but I’m happy to leave it to those places.

  10. Given that a ThrowHelper call is about as easy to write and read as an explicit throw statement, I don’t really see why you would ever not want to save those 30-60 bytes of machine code per occurrence. It’s not like you’re introducing a complex reformulation of an algorithm — it’s usually a simple textual replacement, one statement vs one method call.

    Once I had realized how costly throw is, I just replaced throw with ThrowHelper everywhere. There’s no downside, you make your binary image smaller, and the JITter gets new chances at inlining — what’s not to like?

  11. It’s not as easy to read, because it’s something else to learn that’s non-standard. Everyone knows what a throw statement does – and that includes the compiler, which knows that execution won’t go beyond it. (Think about the end of a switch case, for example.)

    How do you throw an arbitrary exception with a message in ThrowHelper? Does it use generics and reflection together for speed and assume the exception will have the appropriate constructor? Or do you have one method for each type of exception you want to throw?

  12. @Chris: Having given my skeptical view, I will try it for protocol buffers, where efficiency really is important and inlining could make a significant difference. I don’t mean to seem ungrateful in my comments :)

  13. No problem, and I’m not saying you’re guaranteed vast performance improvements or anything, just that this particular micro-optimization is about as simple and harmless as it gets, so if you start anywhere you might as well start here. :)

    Good point about switch/case statements, the C# compiler unfortunately can’t figure out that a ThrowHelper method won’t return (and I couldn’t find a method attribute to that effect), so you would need an extra break/return statement.

    As for the implementation, I’m not using any generics, just a separate method for every exception type and possible argument combination I want to throw. That’s also how the BCL implements ThrowHelper, by the way. There actually aren’t all that many common types — Argument/Null/OutOfRange and InvalidOperation cover most possibilities. Throw in some I/O-related and application-specific exceptions, and you’re pretty much done.

    The syntax follows the statement syntax, e.g. ThrowHelper.ThrowArgumentException. I’m adding a …WithFormat to the name for overloads that specify a format string with formatting arguments because I found that the overloads can get too confusing otherwise. Of course that’s a question of personal taste.

  14. @Jon,

    No, I don’t explicitly call Format when using Console.WriteLine, but only because the overload is already there, so I might as well use it. That is not the same thing as saying that I think it was worth creating that overload in the first place. Why create the extra maintenance overhead to every method which takes a string, when String.Format is already documented and well known?

  15. @David: Simply for the sake of convenience. We do plenty of things for the sake of convenience – adding a couple of overloads to places where they’re going to be really, really useful seems appropriate to me.

  16. Convenience is one thing, but where do you draw the line? Like I said, I don’t want to see every single method in the framework which takes a string overloaded to also take a format string. If that’s not what you’re advocating, then I guess I don’t see why you think that this particular overload is any more useful than any other method anywhere in the framework that takes a string. Especially since it would have to duplicated in every single Exception class to have any real value.

  17. @David: I agree there’s a line to be drawn, and that it would be a pain to have the overloads everywhere. I just find myself wanting it so often that it annoys me.

    Perhaps, as Steve Cooper says, the answer isn’t to change all the methods, but to give language support for string.Format. Hmm.

  18. In some .NET programming languages, at least in Boo ( http://boo.codehaus.org/ ), there is string interpolation expressions, which allows for any string:

    “Expected to read ${requiredSize} bytes but only ${bytesRead} were available”

    Easy to use (moreso than Python’s %) and easy to read.

  19. I presume the constructor would look like this

    Exception(string message, params object[] values)

    If so, how would this work?

    try
    {
    throw InvalidOperationException();
    }
    catch(Exception e)
    {
    throw new MyException(e.Message, e);
    }

    Is “e” a format value, or an inner exception?

    :-)

  20. @Peter: I’m pretty sure (though I haven’t checked) that the rules for picking the “best” method would do the right thing there.

Comments are closed.