在 C# 中使用 lambda 进行流畅的接口配置
许多开源项目使用 Configuration 类和 lambda 来阐明复杂对象的配置。以公共交通为例。一个简单的配置就像这样。
Bus.Initialize(sbc =>
{
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.VerifyMsDtcConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.ReceiveFrom("msmq://localhost/test");
});
当您将鼠标悬停在 Visual Studio 中的 Initialize
上时,它会显示方法调用的参数是 Action
。我想知道是否有人可以展示一个如何在类上使用此模式的简单示例。我什至不知道如何称呼这种类型的模式,而且我的“GoogleFu”尚未运行。在这种特殊情况下,我意识到该方法是在单例模式上运行的。但我同意它作为类的实例方法。
Many open source projects use a Configuration class and lambda's to clarify configuring a complex object. Take Mass Transit for example. A simple configuration would be like so.
Bus.Initialize(sbc =>
{
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.VerifyMsDtcConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.ReceiveFrom("msmq://localhost/test");
});
When you hover over Initialize
in Visual Studio it says the parameter for the method call is Action<ServiceBusConfigurator>
. I was wondering if anyone could show a simple example of how to use this pattern on a class. I don't even know what to call this type of pattern and my "GoogleFu" is not working as of yet. In this particular case I realize the method is operating on a singleton pattern. But I am ok with it being an instance method on a class.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Action
是一种方法,它接受ServiceBusConfigurator
类型的单个参数,对该实例执行“操作”操作,并且不返回任何内容 (void< /代码>)。
.NET BCL(从 3.5 开始)附带预定义的通用委托签名:
Action
、Action
(等等),用于不支持的方法返回一个值,以及Func
、Func
(等)用于接受方法零个或多个参数并返回Tresult
类型的单个结果实例。当您创建接受委托的方法时,您允许方法的调用者传递的不仅仅是数据参数 - 您的方法实际上将一部分责任委托给外部代码。在您的情况下,
Bus.Initialize
创建ServiceBusConfigurator
(sbc
) 的实例,然后使用sbc< 调用指定的操作/code> 实例作为参数。
这基本上可以让您的方法控制配置类实例的生命周期。由调用者填写详细信息,但实际实例由您的类提供:
好处是您的构建实例(在本例中为虚构的 IBus)无法进一步修改。配置实例只会很快创建,传递给外部方法,然后用于创建不可变的最终对象:
上面一行中需要注意的两件事:
匿名方法内的代码包装在传递给该方法的委托内。在
Configure
方法实际调用它之前,它不会执行。cfg
参数由Configure
方法创建并传递给 lambda。该方法返回后,该对象不再存在(或被包装在结果对象内)。An
Action<ServiceBusConfigurator>
is a method which accepts a single parameter of typeServiceBusConfigurator
, does an "action" operating on that instance, and returns nothing (void
)..NET BCL (starting from 3.5) comes with predefined generic delegate signatures:
Action<T>
,Action<T1, T2>
(etc.) for methods which don't return a value, andFunc<Tresult>
,Func<T, Tresult>
(etc.) for methods accepting zero of more parameters and returning a single result instance of typeTresult
.When you create a method which accepts a delegate, you allow callers of your method to pass more than just data parameters - your method actually delegates a part of responsibility to external code. In your case,
Bus.Initialize
creates an instance ofServiceBusConfigurator
(sbc
), and then calls the specified action with thesbc
instance as the parameter.This basically lets your method control the lifetime of the configuration class instance. It is up to the caller to fill in the details, but actual instance is provided by your class:
The benefit is that your built instance (the imaginary
IBus
in this case) cannot be modified further. Configuration instance is only created shortly, passed to an external method, and then used to create an immutable final object:Two things to note in the line above:
The code inside the anonymous method is wrapped inside a delegate passed to the method. It is not executed until the
Configure
method actually calls it.The
cfg
parameter is created by theConfigure
method and passed to the lambda. After the method returns, this object doesn't exist anymore (or is wrapped inside the resulting object).补充一下其他人所说的,这是流畅界面的“入口点” 。使用 Action 回调来实现此目的的方法是一种隔离流畅接口的好方法,同时又非常可扩展。
To add to what others have said, this is an "entry point" into a fluent interface. The approach of using an Action callback to achieve this is a nice way of isolating the fluent interface in a way that is at the same time very extensible.
这类似于工作单元模式,通常与事务和持久性场景相关,但似乎适合您的示例。
以下引文摘自Martin Fowler 对模式的定义:
如果您将业务事务更改为初始化,将数据库更改为配置,您可以更好地了解正在发生的情况。此外,将操作(案例中的委托)视为原子操作:要么完全应用新配置,要么保持当前配置不变。
如前所述,操作本身并未显式触及
Bus
。即使不知道所涉及的类的详细信息,我们也可以猜测这种交互是如何发生的:Initializa()
方法返回之前,可以读取ServiceBusConfigurator
(最有可能的);Bus
可能会实现/扩展ServiceBusConfigurator
,以便Initialize()
可以将this
作为调用操作的参数传递(可能性较小);Bus
可能是静态的,并且对ServiceBusConfigurator
可见,而后者又可以在调用ReceiveFrom() 时更改
(极其复杂,我希望这不太可能)。Bus
的配置属性这些是我现在突然想到的一些策略。可能会建议许多其他人!
This resembles the Unit of Work pattern, which is commonly associated with transactions and persistence scenarios, but seems to fit to your example.
The following citation was taken from Martin Fowler's definition of the pattern:
If you change business transaction for initialization and database for configuration, you can get a better idea of what's going on. Additionally, think of the action (the delegate in case) as an atomic operation: either the new configuration is fully applied, or the current configuration is kept unchanged.
As noted, the action itself doesn't explicitly touch
Bus
. Even without knowing the details of the involved classes, we can guess how this interaction occurs:ServiceBusConfigurator
may be read after the action is invoked, before theInitializa()
method returns (most likely);Bus
might implement/extendServiceBusConfigurator
, so thatInitialize()
can passthis
as the argument of the invoked action (less likely);Bus
may be static and visible toServiceBusConfigurator
, which in turn can change the configuration properties ofBus
upon a call toReceiveFrom()
(extremely convoluted and, I hope, very unlikely).These are some strategies that just popped up my mind right now. Many others may be suggested!