Complex Assertions using C# 3.0
Recently I attempted to implement a declarative predicate checking system to allow design by contract (DBC) within C# 3.0. I was not successful due to a limitation in the kind of parameters one can pass to an Attribute constructor in .NET (no lambdas). I thought I’d just follow that up with a simpler model based on extension methods.
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">class</span> <span style="color:rgb(43,145,175);">Predicates
</span>{
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">void</span> Assert<T>(<span style="color:rgb(0,0,255);">this</span> T obj, <span style="color:rgb(43,145,175);">Func</span><T, <span style="color:rgb(0,0,255);">bool</span>> pred)
{
<span style="color:rgb(0,0,255);">if</span> (!pred(obj))
<span style="color:rgb(0,0,255);">throw</span> <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ApplicationException</span>();
}
}
This simple extension method can be attached to any object allowing Ensures and Requires like this.
<span style="color:rgb(0,0,255);">int</span> MyIntProp{<span style="color:rgb(0,0,255);">get</span>;<span style="color:rgb(0,0,255);">set</span>;}
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">void</span> MyMethod()
{
<span style="color:rgb(0,0,255);">this</span>.Assert(x => x.MyIntProp < 10);
MyIntProp += 10;
<span style="color:rgb(0,0,255);">this</span>.Assert(x => x.MyIntProp >= 10);
}
This is a nice clear implementation that is good for validation. But I think that I can extend it further by exploiting serialization of snapshots within a scope to allow before/after analysis within the scope. Here’s what I want to be able to write:
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">void</span> MyBetterMethod()
{
<span style="color:rgb(0,0,255);">this</span>.Require(x => x.MyIntProp < 10);
MyIntProp += 10;
<span style="color:rgb(0,0,255);">this</span>.Ensure(x => x.MyIntProp == x.before().MyIntProp + 10);
}
Well, my recent writings about the Ambient Context pattern might give you a clue about how I would manage the scope. The first thing I need to be able to do is store a snapshot of the object before it gets tested by the Require. I chose an IDisposable object so that I can clean up after myself without the danger of having the serialized guts of objects lying around everywhere.
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">class</span> <span style="color:rgb(43,145,175);">PredicateScope</span> : <span style="color:rgb(43,145,175);">IDisposable
</span>{
[<span style="color:rgb(43,145,175);">ThreadStatic</span>]
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(43,145,175);">Stack</span><<span style="color:rgb(43,145,175);">PredicateScope</span>> Scopes = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">Stack</span><<span style="color:rgb(43,145,175);">PredicateScope</span>>();
<span style="color:rgb(0,0,255);">internal</span> <span style="color:rgb(0,0,255);">readonly</span> <span style="color:rgb(43,145,175);">Dictionary</span><<span style="color:rgb(0,0,255);">object</span>, <span style="color:rgb(0,0,255);">string</span>> Snapshots = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">Dictionary</span><<span style="color:rgb(0,0,255);">object</span>, <span style="color:rgb(0,0,255);">string</span>>();
<span style="color:rgb(0,0,255);">internal</span> <span style="color:rgb(0,0,255);">readonly</span> <span style="color:rgb(43,145,175);">Dictionary</span><<span style="color:rgb(0,0,255);">object</span>, <span style="color:rgb(0,0,255);">object</span>> DeserializedSnapshots = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">Dictionary</span><<span style="color:rgb(0,0,255);">object</span>, <span style="color:rgb(0,0,255);">object</span>>();
<span style="color:rgb(0,0,255);">public</span> PredicateScope(<span style="color:rgb(0,0,255);">params</span> <span style="color:rgb(0,0,255);">object</span>[] objects)
{
<span style="color:rgb(0,0,255);">foreach</span> (<span style="color:rgb(0,0,255);">object</span> obj <span style="color:rgb(0,0,255);">in</span> objects)
{
Snapshots.Add(obj, CreateSnapShot(obj));
}
Scopes.Push(<span style="color:rgb(0,0,255);">this</span>);
}
<span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">string</span> CreateSnapShot(<span style="color:rgb(0,0,255);">object</span> obj)
{
<span style="color:rgb(43,145,175);">XmlSerializer</span> serializer = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">XmlSerializer</span>(obj.GetType());
<span style="color:rgb(43,145,175);">StringWriter</span> sr = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">StringWriter</span>();
serializer.Serialize(sr, obj);
<span style="color:rgb(0,0,255);">return</span> sr.ToString();
}
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">void</span> Dispose()
{
Snapshots.Clear();
Scopes.Pop();
}
}
You just pass the scope object whatever objects you intend to test later on. It takes snapshots of the objects and stores them away for later reference. It also maintains a stack, so it can be nested. Strictly speaking this is unnecessary, but I figure it might come in handy later on.
My Assertion methods are pretty much the same, but they’re now augmented by a “before” extension method that will get a snapshot keyed to the object it’s extending, and return that instead.
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">class</span> <span style="color:rgb(43,145,175);">Predicates
</span>{
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">void</span> Require<T>(<span style="color:rgb(0,0,255);">this</span> T obj, <span style="color:rgb(43,145,175);">Func</span><T, <span style="color:rgb(0,0,255);">bool</span>> pred)
{
<span style="color:rgb(0,0,255);">if</span> (!pred(obj))
<span style="color:rgb(0,0,255);">throw</span> <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ApplicationException</span>();
}
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">void</span> Ensure<T>(<span style="color:rgb(0,0,255);">this</span> T obj, <span style="color:rgb(43,145,175);">Func</span><T, <span style="color:rgb(0,0,255);">bool</span>> pred)
{
<span style="color:rgb(0,0,255);">if</span> (!pred(obj))
<span style="color:rgb(0,0,255);">throw</span> <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ApplicationException</span>();
}
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">static</span> T before<T>(<span style="color:rgb(0,0,255);">this</span> T obj) <span style="color:rgb(0,0,255);">where</span> T : <span style="color:rgb(0,0,255);">class
</span> {
<span style="color:rgb(0,0,255);">if</span> (obj == <span style="color:rgb(0,0,255);">null</span>)
<span style="color:rgb(0,0,255);">throw</span> <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">ArgumentNullException</span>(<span style="color:rgb(163,21,21);">"obj cannot be null"</span>);
<span style="color:rgb(43,145,175);">PredicateScope</span> ctx = <span style="color:rgb(43,145,175);">PredicateScope</span>.Scopes.Peek();
<span style="color:rgb(0,0,255);">if</span> (ctx == <span style="color:rgb(0,0,255);">null</span>) <span style="color:rgb(0,0,255);">return</span> <span style="color:rgb(0,0,255);">default</span>(T);
<span style="color:rgb(0,0,255);">if</span> (ctx.DeserializedSnapshots.ContainsKey(obj))
<span style="color:rgb(0,0,255);">return</span> ctx.DeserializedSnapshots[obj] <span style="color:rgb(0,0,255);">as</span> T;
<span style="color:rgb(0,0,255);">string</span> serializedObject = ctx.Snapshots[obj];
<span style="color:rgb(43,145,175);">XmlSerializer</span> ser = <span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">XmlSerializer</span>(<span style="color:rgb(0,0,255);">typeof</span>(T));
<span style="color:rgb(43,145,175);">XmlReader</span> reader = <span style="color:rgb(43,145,175);">XmlReader</span>.Create(<span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">StringReader</span>(serializedObject));
<span style="color:rgb(0,0,255);">object</span> result = ser.Deserialize(reader);
ctx.DeserializedSnapshots[obj] = result;
<span style="color:rgb(0,0,255);">return</span> result <span style="color:rgb(0,0,255);">as</span> T;
}
}
The before method gets the snapshot out of the scope, and returns that. You can then use it in your assertions in exactly the same way as the original object.
[<span style="color:rgb(43,145,175);">TestFixture</span>, <span style="color:rgb(43,145,175);">DataContract</span>]
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">class</span> <span style="color:rgb(43,145,175);">MyClass
</span>{
[<span style="color:rgb(43,145,175);">DataMember</span>]
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">int</span> MyInt { <span style="color:rgb(0,0,255);">get</span>; <span style="color:rgb(0,0,255);">set</span>; }
[<span style="color:rgb(43,145,175);">Test</span>]
<span style="color:rgb(0,0,255);">public</span> <span style="color:rgb(0,0,255);">void</span> MyMethod()
{
<span style="color:rgb(0,0,255);">using</span> (<span style="color:rgb(0,0,255);">new</span> <span style="color:rgb(43,145,175);">PredicateScope</span>(<span style="color:rgb(0,0,255);">this</span>))
{
<span style="color:rgb(0,0,255);">this</span>.Require(x => x.MyInt < 10);
MyInt += 10;
<span style="color:rgb(0,0,255);">this</span>.Ensure(x => MyInt == x.before().MyInt + 10);
}
}
}
Obviously, for production use you’d have to ensure this stuff didn’t get run by using ConditionalAttribute. It would affect performance. But for debugging it can be a godsend.
Andrew Matthews
SEMANTICWEB