views:

442

answers:

8

I am writing a (very small) framework for checking pre- and postconditions of methods. Entry points are (they could be easily be methods; that doesn't matter):

public static class Ensures {
    public static Validation That {
        get { ... }
    }
}

public static class Requires {
    public static Validation That {
        get { ... }
    }
}

Obviously, checking the postconditions may be expensive, and isn't actually necessary, when the method isn't buggy. So I want a method which works like this:

public static class Ensures {
    [ConditionalCallingCode("DEBUG")]
    public static Validation ThatDuringDebug {
        get { ... }
    }
}

where ConditionalCallingCodeAttribute means that this method should only run when the calling code is compiled with the DEBUG symbol defined. Is this possible?

I want client code to look like this:

public class Foo {
    public void Bar() {
        ... // do some work
        Ensures.That // do these checks always
            .IsNotNull(result)
            .IsInRange(result, 0, 100);

        Ensures.WhileDebuggingThat // only do these checks in debug mode
            .IsPositive(ExpensiveCalculation(result));

        return result;
    }
}

Of course, I can simply not provide WhileDebuggingThat. Then the client code would look like this:

public class Foo {
    public void Bar() {
        ... // do some work
        Ensures.That // do these checks always
            .IsNotNull(result)
            .IsInRange(result, 0, 100);

        #ifdef DEBUG
        Ensures.That // only do these checks in debug mode
            .IsPositive(ExpensiveCalculation(result));
        #endif

        return result;
    }
}

This is the fallback plan if nothing else works out, but it breaks DRY really badly.

As I understand it, marking WhileDebuggingThat with [Conditional("DEBUG")] will emit (or not) this method depending on whether DEBUG is defined during the compilation of the library, not of the assemblies which reference this library. So I could do this and then write documentation telling the library users to link debug builds of their code with the debug build of the library, and release builds with release builds. This doesn't strike me as the best solution.

Finally, I could tell the library users to define this class inside their projects:

using ValidationLibrary;
public static class EnsuresWhileDebugging {
    [Conditional("DEBUG")]
    public static Validation That() {
        return Ensures.That;
    }
}

This should work as well, as far as I see, but still requires breaking the DRY principle, if only slightly.

+3  A: 

Is this anything that the normal ConditionalAttribute doesn't do for you, aside from working on a property instead of a method? You may well need to change the way things are called so that you've got methods instead of properties - and the fact that it returns a value may cause issues.

It would help a lot if you'd show how your framework is used - currently we've not got a lot to work with.

Another thing to consider would be supplying a variety of binary builds of your library - so that the caller can just supply a different version which doesn't actually do any checking. Again though, it's hard to tell with only the code you've provided.

Jon Skeet
A: 

I have not tried this since I am gonna bathe and leave the house.

  1. Call Assembly.GetCallingAssembly() to get the assembly where the method (class) calling your current executing method comes from.
  2. Run a check on that Assembly object to see if it is Release or Debug build.
icelava
A: 

It sounds like most of what you're doing is already covered using Debug.Assert().

For that matter, this code would only ever run in debug mode (but you have to put up with catch-block slowness):

try
{
     Debug.Assert(false);
}
catch (Exception e)
{
     // will only and always run in debug mode

}
Joel Coehoorn
Debug.Assert() works for postconditions only. If I want to have same API for verifying pre- and postconditions, or to easily switch between debug-only and always-checked postconditions, it isn't suitable.
Alexey Romanov
+1  A: 

Any solution that is found here would be slower than the actual checks. Also, since it would not be build into the compiler like ConditionalAttribute, the parameters would still be calculated. If the postconditions could be very complicated, such as

Ensures.That.IsPositive(ExpensiveCalculation(result));

You might consider using icelava's suggestion to reflect on the calling assembly to find if it is built in debug or release - but then you must use some sort of delegate to delay the calculation - to ensure that it is only done when needed. e.g.:

Ensures.WhileDebugging.That. IsPositive(() => ExpensiveCalculation(result));

The IsPositive function should run the lambda and check its result, only after reflecting to find out if it should be calculated.

configurator
Yes, but I consider even that an unacceptable overhead for this specific application.
Alexey Romanov
Which part is the overhead?
configurator
A: 

It appears that what I want is just not available. I will probably settle for providing an implicit conversion from Validation to bool, so that validation checking may be wrapped in Debug.Assert().

Alexey Romanov
A: 

The Debug Assert method can be set/changed using a bool even after the program is compiled, if for example the value is taken from a project user seting:

Debug.Assert(!Properties.Settings.Default.UseAutoDebug);
A: 

I'm not sure but I think you could use ConditionalAttribute for this: whether to emit call or not to emit will depend on type of build of user, not your library. You can check this with Reflector or ILDasm: compile your samples and in Reflector (ILDasm) look whether call is emitted or not in sample project.

Kaagle
A: 

Hi I have this occur: Project A call 1 function of B. B include this function: Assembly.GetCallingAssembly().FullName If build B at mode debug then running, this function return name of Project A, if build at mode release than return name of project B. I dont know reason of this occur. Please support me Thanks