BooDesign By Contract in 20 lines of code
Now, before Greg hurls a modopt on me, I want to be clear that this isn't the same thing that Spec# is doing. But it is a very cool way to specify constraints that must always be valid when a method exists.
Here is the code:
[AttributeUsage(AttributeTargets.Class)] class EnsureAttribute(AbstractAstAttribute): expr as Expression def constructor(expr as Expression): self.expr = expr def Apply(target as Node): type as ClassDefinition = target for member in type.Members: method = member as Method continue if method is null block = method.Body method.Body = [| block: try: $block ensure: assert $expr |].Block
And the usage:
[ensure(name is not null)] class Customer: name as string def constructor(name as string): self.name = name def SetName(newName as string): name = newName
Now, any attempt to set the name to null will cause an assertion exception. This technique is quite powerful, and very easy to use. A few years ago I wrote a design by contract implementation for boo that was far more ambitious (handling inheritance, etc). I remember it being much more complicated, and while things like quasi quotation do make it easier, it is not that big a change.
I think that mostly it is the way I write code now, striving to simplicity is something that I am trying to apply recently, and I think it works.
More posts in "Boo" series:
- (08 Jan 2018) Serious Cryptography
- (25 Nov 2004) Open Source .NET Development
Comments
wow, that right there is basically what I just said on the alt.net list is a capability I wish would be available for C# 4.0.
How do you come up with this stuff so fast?
Bill,
Take a look at the code and you'll understand how I come with it so fast. It is simple, when you know how.
That I realize, I was referring to the "person X thinks about it, you have already made a blog post about it" concept. It is like you are in all of our heads.
(In this case your post came before my comment on the list, I just hadn't seen it yet.)
That looks cool! I guess it would be easy to put them on methods too, such as:
[invariant(name is not null)]
class Customer:
[precondition(orders is not empty)], [postcondition(discount is not null)]
def ApplyDiscount():
Tobin,
Yes, you can put this on methods as well.
How about something like this:
[ensure(name is not null) and (name is not empty)]
?
concatenations of operators?
This will work:
[ensure(name is not null and name is not empty)]
Do you have unit tests for the dynamic code?
When is EnsureAttribute.Apply(...) called? and by what?
During compilation, by the compiler
I'm guessing that Apply is inherited from AbstractAstAttribute and the compiler has a "built-in rule" to call Apply on all AbstractAstAttributes?
Comment preview