使用C#中的lambdas进行流畅的接口配置



许多开源项目使用Configuration类和lambda来澄清复杂对象的配置。以公共交通为例。一个简单的配置就是这样。

Bus.Initialize(sbc =>
        {
            sbc.UseMsmq();
            sbc.VerifyMsmqConfiguration();
            sbc.VerifyMsDtcConfiguration();
            sbc.UseMulticastSubscriptionClient();
            sbc.ReceiveFrom("msmq://localhost/test");
        });

当您将鼠标悬停在Visual Studio中的Initialize上时,它会显示方法调用的参数为Action<ServiceBusConfigurator>。我想知道是否有人可以展示一个如何在类中使用此模式的简单示例。我甚至不知道该怎么称呼这种模式,而且我的"GoogleFu"还不起作用。在这种特殊的情况下,我意识到该方法是在单例模式上操作的。但我同意将它作为类上的实例方法。

Action<ServiceBusConfigurator>是一种方法,它接受ServiceBusConfigurator类型的单个参数,对该实例执行"操作",并且不返回任何内容(void)。

.NET BCL(从3.5开始)具有预定义的通用委托签名:Action<T>Action<T1, T2>(等)用于不返回值的方法,Func<Tresult>Func<T, Tresult>(等)适用于接受零个或多个参数并返回类型为Tresult的单个结果实例的方法。

当您创建一个接受委托的方法时,您允许方法的调用方传递的不仅仅是数据参数——您的方法实际上将部分责任委托给了外部代码。在您的情况下,Bus.Initialize创建ServiceBusConfiguratorsbc)的实例,然后使用sbc实例作为参数调用指定的操作。

这基本上允许您的方法控制配置类实例的生存期。由调用者填写详细信息,但实际实例由您的类提供:

 // this is not actual mass transit source code
 public class BusCreator
 {
     public static IBus Initialize(Action<IConfiguration> action)
     {
         // create the config instance here
         IConfiguration config = CreateDefaultConfig();
         // let callers modify it
         action(config);
         // use the final version to build the result
         return config.Build()
     }
 }

这样做的好处是,您构建的实例(在本例中为假想的IBus)无法进一步修改。配置实例只会很快创建,传递给外部方法,然后用于创建一个不可变的最终对象:

 IBus result = BusCreator.Configure(cfg => cfg.BusType = BusType.MSMQ);

在上面的行中需要注意两件事:

  1. 匿名方法内部的代码封装在传递给该方法的委托中。直到Configure方法实际调用它,它才会被执行

  2. cfg参数由Configure方法创建并传递给lambda。方法返回后,该对象不再存在(或者被包裹在生成的对象中)。

为了补充其他人的说法,这是进入流畅界面的"入口点"。使用Action回调来实现这一点的方法是一种很好的隔离fluent接口的方法,同时具有很强的可扩展性。

这类似于工作单元模式,它通常与事务和持久性场景相关,但似乎适合您的示例。

以下引文取自马丁·福勒对模式的定义:

工作单元跟踪您在可能影响数据库的业务事务中所做的一切。完成后,它会计算出由于您的工作而需要更改数据库的所有操作。

如果您更改初始化业务事务配置中的database,您可以更好地了解发生了什么。此外,将操作(在这种情况下为委托)视为原子操作:要么完全应用新配置,要么保持当前配置不变。

如前所述,动作本身并没有明确触及Bus。即使不知道所涉及类的细节,我们也可以猜测这种交互是如何发生的:

  • ServiceBusConfigurator可以在动作被调用之后、在Initializa()方法返回之前(最有可能)被读取
  • Bus可能实现/扩展ServiceBusConfigurator,使得Initialize()可以传递this作为被调用动作的参数(可能性较小)
  • Bus可能是静态的,并且对ServiceBusConfigurator可见,这反过来又可以在调用ReceiveFrom()时更改Bus的配置属性(非常复杂,我希望非常不可能)

这些是我现在突然想到的一些策略。可能会有许多其他建议!

最新更新