Отказ от ответственности 1: Сумасшедший педантичный изгиб языка впереди.
Отказ от ответственности 2: Любым клиентам, присутствующим или будущим - я не выставляю вам счет за это.
Хорошо, поэтому на самом деле это не необходимо , но я бездельничаюс созданием плагинов для xunit.net и появляется интересный сценарий
В настоящее время пример расширения SubSpec, поставляемого с исходным кодом, работает примерно так:
[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));
}
Это не красивоэлегантный C #, который я знаю и люблю - мне не нравится объявлять неинициализированные переменные, и я предпочитаю не объявлять их вообще.Я бы предпочел сделать что-то вроде этого:
[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));
}
В этом случае метод расширения Context () будет иметь подпись void Context(this string, Action setUpWith)
и подпись T Context<T>(this string, Func<T> setUpWith)
.Интересная проблема возникает в реализации, хотя, поскольку делегат setUpWith фактически не выполняется в настоящее время, он просто сохраняется, а затем выполняется пользовательским атрибутом Спецификации.Это означает, что на самом деле нет Т для возврата.Тем не менее, поскольку сохраненные делегаты выполняются по порядку, я могу гарантировать, что он будет существовать к моменту вызова делегата в методе Do ().
Так что мне хотелось бы вернуть динамический проксидля T, но это на самом деле неосуществимо, поскольку я хотел бы иметь возможность использовать анонимные типы, которые сделали бы T запечатанным.
Теперь, на земле C ++, я считаю, что можно было бы выделить память для объекта и вернуть «объект» в качестве ссылки на этот бит пространства, который затем будет заполнен в конце концов.По сути, это то, что произошло бы, если бы я передал T в качестве параметра ref (но тогда я должен был бы объявить его, отвергая точку).
Тем не менее, есть углы C #, которые я не исследовал.Кто-нибудь может придумать способ выжать из этого желаемый синтаксис?
PS.У меня есть пара решений с небольшими изменениями в синтаксисе, которые я опубликую как ответы ниже.