Disclaimer 1: Crazy pedantic language-bending drivel ahead.
Disclaimer 2: To any clients present or future - I am not billing you for this.
Alright, so this is not really necessary but I'm messing around with creating plugins for xunit.net and an interesting scenario comes up
Currently, the example of the SubSpec extension that ships with the source works something like this:
[Specification]
void calculator_addition() {
Calculator calc = null;
User user = null;
"Given a calculator and a user".Context(()=> {
calc = new Calculator();
user = new User(calculationQuota: 100);
});
"adding 1 + 1".Do(()=>result = calc.Add(1, 1));
"will equal 2".Assert(()=>calc.Result.ShouldEqual(2));
"will decrease user's quota by one".Assert(()=>user.CalculationsLeft.ShouldEqual(99));
}
That's not the beautiful, elegant C# that I know and love - I don't like declaring uninitialized variables and I prefer not to declare them at all. I would much prefer to do something like this:
[Specification]
void calculator_addition() {
var _ =
"Given a calculator and a user".Context(()=> new {
Calc = new Calculator(),
User = new User(calculationQuota: 100),
});
"adding 1 + 1".Do(()=>_.Calc.Add(1, 1));
"will equal 2".Assert(()=>_.Calc.Result.ShouldEqual(2));
"will decrease user's quota by one".Assert(()=>_.User.CalculationsLeft.ShouldEqual(99));
}
In this case, the Context() extension method would have the signature void Context(this string, Action setUpWith)
would have the signature T Context<T>(this string, Func<T> setUpWith)
. An interesting problem arises in implementation though since the setUpWith delegate isn't actually executed at this time, it is merely stored and then executed later by the custom Specification attribute. This means that there is not really a T to return at this time. However, since the stored delegates are executed in order, I can guarantee that it will exist by the time the delegate in the Do() method is called.
So what I would be nice is to return a dynamic proxy for T, but this is not really feasible since one of the things I'd like to be able to do is use anonymous types which would make T sealed.
Now in C++ land I believe that it would be possible to allocate memory to the object and return the "object" as a reference to that bit of space which will then get filled in eventually. This is essentially what would happen if I passed T in as a ref parameter (but then I'd have to declare it defeating the point).
Still, there are corners of C# that I have not explored. Can anyone think of a way to squeeze my desired syntax out of this?
PS. I have a couple solutions with slight modifications to the syntax which I will post as answers below.