【问题标题】:Writing my first DSL in C# and getting hung up on func<T> & Action用 C# 编写我的第一个 DSL 并沉迷于 func<T> 和 Action
【发布时间】:2010-12-20 04:08:53
【问题描述】:

我正在为一个简单的工作工具编写我的第一个 DSL。我正在使用构建器模式来设置复杂的父对象,但在构建父对象的子集合时遇到了砖墙。这是一个示例:

用途:

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

带闭包的示例(我认为这就是它们的名称):

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

示例类(没有子 PourIn() 方法,因为这是我想要弄清楚的。)

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

因此,在我的工作应用程序中,我可以很好地构建复杂的对象,但我一生都无法弄清楚如何为父对象上的子集合获取 lambda 编码。 (在本例中是 expresso 的镜头(子集合))。

也许我在这里混淆了概念,我不介意直截了当;但是,我真的很喜欢它的阅读方式,并想弄清楚如何让它发挥作用。

谢谢, 山姆

【问题讨论】:

  • 老实说;恕我直言,这确实是对DSL的可怕使用。它对我来说很可怕。但我想对每个人来说都是他自己的。
  • 那么你的问题是什么?所有这些代码看起来都是专有的,所以我们无法知道其中的任何含义。比如IncludeApps的参数类型是什么?
  • 你能贴出 IncludeApps 方法的签名吗?
  • 这看起来更像是一个流畅的界面而不是 DSL。
  • 我同意,这个样本很烂,我希望它能传达我的意图,但也许不会。我同意“James Black”的观点,即这可能是一个比 DSL 更流畅的界面。关于“IncludeApps”方法的签名,这就是我的观点,我不确定这应该是什么。这是一个不同对象的子集合,所以我想你可以随意提出任何样本。

标签: c# lambda action dsl closures


【解决方案1】:

如果 .IncludeApps 接受一组 AppRegistrations 会怎样

IncludeApps(params IAppRegistration[] apps)

然后

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

所以最终它会看起来像这样......

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

在您的 IncludeApps 方法中,您将检查注册并根据需要创建对象。

【讨论】:

  • 我绝对认为你正朝着正确的方向前进,但我想在闭包中使用 c# 的 System.Action 或 System.Func 对象来解决这个问题。这是我试图抓住的概念。谢谢。
  • 这不是使用闭包的好地方。当您想要定义一个 action 时,Lambda 表达式很有用,它将根据一些未知参数发生。在这里,一切都是已知的,您并没有真正定义一个动作。您只想在手机上注册一个应用程序。
  • 很好地调用参数。我在想同样的事。你打败了我。
  • 你绝对不是在处理闭包。有关闭包在 c# bit.ly/81BfpW 中的行为的信息,请参阅这篇文章
【解决方案2】:

走代理路线也许这样可行?

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

如果您没有设置委托路线,参数可能会起作用?

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

【讨论】:

    【解决方案3】:

    好的,所以我想出了如何使用额外的表达式构建器来编写我的 DSL。这就是我希望我的 DSL 读取的方式:

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

    这是我的及格测试:

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

    这是我的 CoffeeExpressionBuilder DSL 类:

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

    欢迎提出任何建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多