2009-11-25 10 views
6

Sto prendendo una brutta copia scrivendo il mio primo DSL per un semplice strumento al lavoro. Sto usando il pattern builder per configurare l'oggetto genitore complesso, ma sto correndo su muri di mattoni per costruire le collezioni figlio dell'oggetto genitore. Ecco un esempio:Scrivere il mio primo DSL in C# e rimanere agganciato su func <T> e azione

Usa: metodo

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

classe Sample (senza il bambino pourin() come questo:

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

campione con chiusura (credo che sia come si chiamano) è quello che sto cercando di capire.)

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; 
    } 
} 

Quindi nella mia app per lavoro ho il complesso oggetto di costruzione appena bene, ma non posso per la vita di me capire come ottenere il lambda codificato per la sub collection sull'oggetto genitore. (in questo esempio sono gli scatti (collezione bambino) di expresso).

Forse sto confondendo i concetti qui e non mi importa di essere diretto; tuttavia, mi piace molto come si legge e vorrei capire come farlo funzionare.

Grazie, Sam

+0

Devo essere sincero; questo è veramente un uso terribile di un DSL, IMHO. Mi legge orribilmente. Ma a ciascuno il suo, suppongo. –

+0

Quindi qual è il tuo problema? Tutto questo codice sembra proprietario, quindi non abbiamo modo di sapere cosa significhi. Ad esempio, qual è il tipo di parametro di IncludeApps? – tster

+0

Puoi pubblicare la firma per il metodo IncludeApps? –

risposta

2

Ok, quindi ho capito come scrivere il mio DSL utilizzando un generatore di espressioni aggiuntive. Questo è come volevo che il mio DSL a leggere:

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

Ecco il mio test di passaggio:

[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)); 
    } 
} 

Ed ecco la mia classe CoffeeExpressionBuilder DSL (s):

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; 
    } 
} 

Tutti e tutti i suggerimenti prego.

2

E se .IncludeApps accettato una serie di AppRegistrations

IncludeApps(params IAppRegistration[] apps) 

poi

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; 
    } 
} 

così alla fine sarebbe simile a questa ...

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

All'interno del metodo IncludeApps ispezioneresti le registrazioni e creerai gli oggetti come richiesto.

+0

Penso che stiate andando nella giusta direzione, ma mi piacerebbe risolvere questo problema usando gli oggetti System.Action o System.Func di C# in una chiusura. Questo è il concetto che sto cercando di capire. Grazie. – sam

+2

Questo non è un posto utile per usare la chiusura. Le espressioni lambda sono utili quando si desidera definire un'azione che si verificherà secondo un parametro sconosciuto. Qui, tutto è noto e tu non stai definendo veramente un'azione. Vuoi solo registrare un'app sul telefono. – tster

+0

Buona chiamata sui parametri. Stavo pensando la stessa cosa. Mi hai battuto sul tempo. –

1

Per andare al percorso delegato forse qualcosa del genere potrebbe funzionare?

Se non si è impostato sulla rotta del delegato, i parametri potrebbero funzionare?

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; 
    } 
}