assert instead of throw

Nov 30, 2011 at 11:43 PM
Edited Nov 30, 2011 at 11:48 PM

The library always throws if a pre- or post-condition fails.  But for expensive checks, I want to assert instead. There is no such option, so I use "Debug.Assert()" instead.

I don't want to use the messy "#if DEBUG ..." syntax. Is there a clean way to add this functionality? Any extension points I can hook onto?

PS: I know how to extend the library, as I've added many of my own extension methods. But debug-only targetting has me at a loss...

Coordinator
Dec 1, 2011 at 7:25 AM
Edited Dec 4, 2011 at 9:14 AM

Just add this code to your project:

Update [2011-12-04]: The following code is incorrect. Please see further comments below.

namespace CuttingEdge.Conditions
{
    using System.Diagnostics;
 
    public static class ConditionAssert
    {
        public static ConditionValidator<T> Requires<T>(T value)
        {
            return new AssertRequiresValidator<T>("value", value);
        }
 
        public static ConditionValidator<T> Requires<T>(T value, string argumentName)
        {
            return new AssertRequiresValidator<T>(argumentName, value);
        }
 
        private class AssertRequiresValidator<T> : ConditionValidator<T>
        {
            internal AssertRequiresValidator(string argumentName, T value)
                : base(argumentName, value)
            {
            }
 
            [DebuggerStepThrough]
            protected override void ThrowExceptionCore(string condition,
                string additionalMessage, ConstraintViolationType type)
            {
                System.Diagnostics.Debug.Assert(false,
                    condition + ". " + additionalMessage);
            }
        }
    }
}

Now you can write things like this:

ConditionAssert.Requires(a, "a").IsLessThan(3);

Cheers.

Dec 1, 2011 at 2:46 PM
Edited Dec 1, 2011 at 2:49 PM

This works brilliantly, cheers mate!

As a note to others, I have called mine "ConditionASSERT" instead of "ConditionAssert" as you've done, to draw attention to it, and it is also similar to the already familiar eyesore "DEBUG" constant. I know it's unconventional, but I think readability and maintainability are more important than coding conventions--and when I'm scanning through some code, I can't miss the fact that its an assert because of the caps.

You should consider putting this in the main library. I know you said you're opposed to it as it is not in line with your original design, but it makes a lot of sense. This code, as well as that needed for post-condition assertions, are sometimes more sensible than throwing. I'd prefer to throw in all cases, but in SOME cases contract checking is sufficient in the design phase, especially for heavy checks (db, results of complicated linq queries, network stuff), and especially for your own libraries--no point in burdening the library's consumers for expensive checks that are already debugged and unit tested. Perhaps as a separate dll that can be optionally used by the library's users, if they want it?

PS I haven't fiddled extensively with this approach yet, but I assume that I can use a similar mechanism for postcondition assertions? If I can get it to work, I'll post the code for others to see (unless you already have that code handy... :) ).

 

 

Coordinator
Dec 4, 2011 at 9:13 AM

I'm sorry, but I made a mistake. The ConditionAssert class is utterly useless. What Debug.Assert (or [ConditionAttribute("DEBUG")] in general) is supposed to do is to remove the check completely, but this is not what happens. The only thing that will get suppressed in release mode is the throwing of the exception (or showing a dialog box, in the case of the Debug.Assert). The verification check itself however, is always executed.

It is this check that you normally want to prevent from happening, usually because of performance reasons. But since the check is always performed, it would be silly to suppress the throwing of the exception in release mode, since at that point we already established a bug in the software, and we’d better throw to prevent further damage to the system.

With the way CuttingEdge.Conditions is currently designed it is impossible to achieve this. Main reason for this is because both the entry point methods (Requires, Ensures) and the validation methods (IsNotNull, IsGreaterThan, etc) return a value. The C# and VB compilers will therefore not allow calls to those methods to be suppressed.

If you really want to suppress certain checks because of performance considerations, you’ve got two options:

1. Use #if DEBUG:

public void DoSomething(IEnumerable<Element> col)
{
#if DEBUG
    Condition.Requires("col").ContainsAll(requeredElements);
#endif
}

2. Move validation to another method:

public void DoSomething(IEnumerable<Element> col)
{
    ValidateArguments(col);
}

[Condition("DEBUG")]
private static void ValidateArguments(IEnumerable<Element> col)
{
    Condition.Requires(col, "col").ContainsAll(requeredElements);
}
I hope this helps.

Dec 4, 2011 at 3:19 PM
Edited Dec 4, 2011 at 3:20 PM

In theory, would the following work?
- add a debug/release flag in "ConditionAssert"
- check that flag in every validator, and throw/assert appropriately

There are dozens of validators, so it would be a PITA to refactor all of them--so is there a base class into which I could place that functionality? Could I do it in "ConditionValidator" and "ConditionValidator<T>", perhaps in the constructor? 

Coordinator
Dec 4, 2011 at 5:53 PM

For this to work, a boolean flag has to be added to the abstract ConditionValidator<T> class, and every single validation method should skip the check when the flag is set. In your local ConditionAssert class you must set this flag in debug mode to prevent any validations from being executed.

Dec 4, 2011 at 9:33 PM

Yes seems good. Only in the ConditionAssert class I would set the flag explicitly, without considering the #DEBUG symbol (as that would be the purpose of that class).

Also, I'd refactor the ConditionValidator class to make the check itself, and avoid having to change the dozens of validators. Then I'd just call the protected validator method from the abstract base class' method. Something like so:

    public validate(...) {
       if (IsDebug) { ... }
       else { validateImpl(); }
    }

    protected abstract validateImpl();  // implemented in the validator subclasses

 

Actually it seems like quite a neat implementation.