Question

I want my object invariant method to throw a specific exception. Does it make sense? Is it possible in C#?

For instance, I have the following code, including class A with invariant method and exception class E. For now class E is not participating in A...

class A {
    int x = 0, y = 1;

    [ContractInvariantMethod]
    private void YisGreaterThanX() {
        Contract.Invariant(x < y);
    }
}

class E : Exception {
}

And what I need is the following. Like Contract.Requires it would be useful to have Contract.Invariant (or may be an attribute constructor, which accepts Exception-derived class).

class A {
    int x = 0, y = 1;

    [ContractInvariantMethod]
    private void YisGreaterThanX() {
        Contract.Invariant<E>(x < y);
    }
}

class E : Exception {
}

Is it a good intention? May be my logic is wrong?

Was it helpful?

Solution

Given that we aren't supposed to be catching Contract failures, and since there isn't an overload for Contract.Invariant with an explicit specified exception, this answer is hopefully hypothetical.

So after all the disclaimers, you could hack something along the following lines, by wiring up a ContractFailed event handler:

Contract.ContractFailed += Contract_ContractFailed();

And in the handler, filter for Invariant failures, and handle the failure and re-throw your exception:

public static void Contract_ContractFailed(object sender,  ContractFailedEventArgs e)
{
    if (e.FailureKind == ContractFailureKind.Invariant)
    {
        e.SetHandled();
        throw new E(e.Message, e.OriginalException);
    }
}

Given that you can't pass much information in the Contract.Invariant definition, if you needed to parameterize the thrown exception, you would need to encode the intended exception into e.g. the bool, string Contract.Invariant overload, or use external global state, such as thread local storage.

All of these are smelly IMO. So to answer your last question, I believe throwing catchable exceptions isn't a good idea at all - your code is being called out of its designed range of state, so there is a bug / validation missing somewhere.

Edit
Noticed subsequently that handling + throwing in the ContractFailed handler is still wrapped in the internal ContractException. So you'll need to unpack, e.g.

catch(Exception ex)
{
   var myException = ex.InnerException;
   // ... do something
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top