[Journal - Check the Flow]

Check the Flow

Tuesday, February 7, 2006

The methods in the Check and Flow modules in Gregor.Core are very similiar. In the first case, an ArgumentException is thrown if some criterion is not met; in the second case, a boolean value returned. There are two problems with this approach:

  1. Both modules get easily out of sync. Some tests are in Flow, but there's no corresponding check in Check, or vice versa.
  2. Throwing System.ArgumentException is fine for checking method parameters, but there are other cases: checking object state may require throwing InvalidOperationException, or a method may not be supported or not implemented for certain cases, and so on.

To address the first problem, all methods could be changed to instance methods, and both modules converted to classes implementing a common interface. There'd be one instance of Check and one of Flow, both being singletons returned from static properties of some module. But that would mean a lot of changes for client code, as well as having mostly identical implementations in both classes.

So I'm thinking about something else. What if all methods remain static, have their return type changed to bool, and the decision whether to throw an exception or return a boolean value (which is the only difference) is made in each method, according to some flag?

That flag could be a static field, which is defined in a base type. Now, modules don't have base types, but any way of abusing inheritance is fine, isn't it? If methods are static, they can't be virtual, but I don't want that anyway. I want one central implementation for each check and test. So the flag must be in the base class. Static fields have a way of being, uhm, static, so how can the field be different?

The solution is to abuse yet another feature of the type system - generics. Static fields exist once per closed constructed type, so I could make the base module a generic type:

public class CheckOrTest<T> where T : Exception, new() {

    private static bool s_ThrowsException = true;

    static CheckOrTest(){
        Flow.InitializeStaticData();
    }

    protected static bool ThrowsException{
        get{
            return s_ThrowsException;
        }
        set{
            s_ThrowsException = value;
        }
    }

    public static bool Reference(object obj){
        if(null == obj){
            if(s_ThrowsException){
                throw new T();
            }else{
                return false;
            }
        }
        return true;
    }

}

Both Check and Flow can be derived from CheckOrTest as (open and closed, respectively) constructed types:

public class Check<T> : CheckOrTest<T> where T : Exception, new() {

}

// default case, compatibility
public class Check : Check<ArgumentException> {

} // class Check


public class Flow : CheckOrTest<Exception> {

    internal static void InitializeStaticData(){
        CheckOrTest<T>.ThrowsException = false;
    }

}

One interesting aspect is that I can't use a static constructor in the "derived" modules (for example, in Flow) to initialize the static field - compare the rules governing just when static constructors are invoked. Therefore, is use the "base" module's static constructor to initialize Flow's copy of s_ThrowsException.

The generic solution also addresses the second problem - using different exception types. For this, Check is also a generic type definition. Here's a usage example:

public static class Program {

    public static void Main(string[] args){
    
        bool b = Flow.Reference(null);
        Console.WriteLine(b);
        
        try{
            Check<InvalidOperationException>.Reference(null);
        }catch(Exception ex){
            Console.WriteLine(ex);
        }
    }
 
}

One open issue is how to feed arguments to exceptions. Since the only constructor constraints are default constructor constraints, the only way to pass messages and such to the exception is to use reflection. The bad news is that the generic Activator.CreateInstance(Of T) method only supports default constructors. But some checking of available constructors is in order - if it can't be done with generic type constraints, it has to be done at runtime via reflection. The way to do it is obtaining ConstructorInfo objects for the exception types (once for every specialization, again in Check's static constructor). Also, I'd have to cast in any case:

public static bool Reference(object obj, string sMessage){
    if(null == obj){
        if(s_ThrowsException){
            // TODO: check constructor parameters
            T ex = (T) Activator.CreateInstance(typeof(T), sMessage);
            throw ex;
        }else{
            return false;
        }
    }
    return true;
}

The whole approach of merging both modules' implementation has one downside: I can't use Check for development-phase assertions, making its methods conditional (System.Diagnostics.ConditionalAttribute). But I don't think using assertions in such a way, that is, for a limited time only, is a good idea, anyway.

Oh well, let me keep on tinkering ...