Writing my first DSL in C # and hanging on func <T> & Action

I am trying to write my first DSL for a simple tool at work. I use the builder pattern to set a complex parent object, but I run into brick walls to create child collections of the parent object. Here's a sample:

Using:

var myMorningCoffee = Coffee.Make.WithCream().WithOuncesToServe(16); 

Closing example (I think they are called):

 var myMorningCoffee = Coffee.Make.WithCream().PourIn( x => { x.ShotOfExpresso.AtTemperature(100); x.ShotOfExpresso.AtTemperature(100).OfPremiumType(); } ).WithOuncesToServe(16); 

Class example (without the child PourIn () method, as that is what I'm trying to figure out.)

  public class Coffee { private bool _cream; public Coffee Make { get new Coffee(); } public Coffee WithCream() { _cream = true; return this; } public Coffee WithOuncesToServe(int ounces) { _ounces = ounces; return this; } } 

So, in my application for working, my complex construction of objects is just fine, but I can’t understand for life how to get a lambda encoded for a subcategory on the parent object. (in this example, these are shots (children's collection) expresso).

Perhaps I misled the concepts here, and I do not mind being straightforward; Nevertheless, I really like how it reads and would like to find out how to do it.

Thanks Sam

+6
closures c # lambda action dsl
Nov 25 '09 at 5:29
source share
3 answers

Ok, so I figured out how to write my DSL using an optional expression constructor. This is how I wanted my DSL to read:

 var myPreferredCoffeeFromStarbucks = Coffee.Make.WithCream().PourIn( x => { x.ShotOfExpresso().AtTemperature(100); x.ShotOfExpresso().AtTemperature(100).OfPremiumType(); } ).ACupSizeInOunces(16); 

Here is my passing test:

 [TestFixture] public class CoffeeTests { [Test] public void Can_Create_A_Caramel_Macchiato() { var myPreferredCoffeeFromStarbucks = Coffee.Make.WithCream().PourIn( x => { x.ShotOfExpresso().AtTemperature(100); x.ShotOfExpresso().AtTemperature(100).OfPremiumType(); } ).ACupSizeInOunces(16); Assert.IsTrue(myPreferredCoffeeFromStarbucks.expressoExpressions[0].ExpressoShots.Count == 2); Assert.IsTrue(myPreferredCoffeeFromStarbucks.expressoExpressions[0].ExpressoShots.Dequeue().IsOfPremiumType == true); Assert.IsTrue(myPreferredCoffeeFromStarbucks.expressoExpressions[0].ExpressoShots.Dequeue().IsOfPremiumType == false); Assert.IsTrue(myPreferredCoffeeFromStarbucks.CupSizeInOunces.Equals(16)); } } 

And here is my DSL class for CoffeeExpressionBuilder:

 public class Coffee { public List<ExpressoExpressionBuilder> expressoExpressions { get; private set; } public bool HasCream { get; private set; } public int CupSizeInOunces { get; private set; } public static Coffee Make { get { var coffee = new Coffee { expressoExpressions = new List<ExpressoExpressionBuilder>() }; return coffee; } } public Coffee WithCream() { HasCream = true; return this; } public Coffee ACupSizeInOunces(int ounces) { CupSizeInOunces = ounces; return this; } public Coffee PourIn(Action<ExpressoExpressionBuilder> action) { var expression = new ExpressoExpressionBuilder(); action.Invoke(expression); expressoExpressions.Add(expression); return this; } } public class ExpressoExpressionBuilder { public readonly Queue<ExpressoExpression> ExpressoShots = new Queue<ExpressoExpression>(); public ExpressoExpressionBuilder ShotOfExpresso() { var shot = new ExpressoExpression(); ExpressoShots.Enqueue(shot); return this; } public ExpressoExpressionBuilder AtTemperature(int temp) { var recentlyAddedShot = ExpressoShots.Peek(); recentlyAddedShot.Temperature = temp; return this; } public ExpressoExpressionBuilder OfPremiumType() { var recentlyAddedShot = ExpressoShots.Peek(); recentlyAddedShot.IsOfPremiumType = true; return this; } } public class ExpressoExpression { public int Temperature { get; set; } public bool IsOfPremiumType { get; set; } public ExpressoExpression() { Temperature = 0; IsOfPremiumType = false; } } 

Any suggestions are welcome.

+2
Dec 01 '09 at 18:39
source share

What if .IncludeApps accepted an array of AppRegistrations

 IncludeApps(params IAppRegistration[] apps) 

then

 public static class App { public static IAppRegistration IncludeAppFor(AppType type) { return new AppRegistration(type); } } public class AppRegistration { private AppType _type; private bool _cost; public AppRegistration(AppType type) { _type = type; } public AppRegistration AtNoCost() { _cost = 0; return this; } } 

so in the end it will look like this ...

 .IncludeApps ( App.IncludeAppFor(AppType.Any), App.IncludeAppFor(AppType.Any).AtNoCost() ) 

Inside the IncludeApps method, you must check the registration and create objects as needed.

+2
Nov 25 '09 at 5:55
source share

To go to the delegate route, maybe something like this will work?

 var aPhone = MyPhone.Create; MyPhone.Create.IncludeApps ( x => { x.IncludeAppFor(new object()); } ); class MyPhone { public MyPhone IncludeApps(Action<MyPhone> includeCommand) { includeCommand.Invoke(this); return this; } } 

If you are not configured on the delegate route, maybe the options will work?

 var anotherPhone = MyPhone.Create.IncludeApps( new IncludeAppClass(AppType.Math), new IncludeAppClass(AppType.Entertainment).AtNoCost()); class MyPhone { internal MyPhone IncludeApps(params IncludeAppClass[] includeThese) { if (includeThese == null) { return this; } foreach (var item in includeThese) { this.Apps.Add(Item); } return this; } } 
+1
Nov 25 '09 at 6:08
source share



All Articles