简体   繁体   English

Contract.Requires和装饰器模式。 如何避免过度检查情况?

[英]Contract.Requires and the Decorator Pattern. How to avoid overchecking conditions?

I currently have a command handling interface that is implemented by a few different classes for different command types. 我目前有一个命令处理接口,该接口由针对不同命令类型的几个不同类实现。 I'm using the Decorator Pattern in conjunction with an IoC container (Unity in my case) to add cross cutting concerns to those handlers, so I created a few classes like: 我将装饰器模式与IoC容器(在我的情况下为Unity)结合使用,以向这些处理程序添加横切关注点,因此我创建了一些类似的类:

  • ValidatorCommandHandlerDecorator ValidatorCommandHandlerDecorator
  • LoggingCommandHandlerDecorator LoggingCommandHandlerDecorator
  • AsyncCommandHandlerDecorator AsyncCommandHandlerDecorator

This is all working as expected and is actually very nice. 这一切都按预期工作,实际上非常好。 The potential problem here is that the code contracts for the interface is checked for every decorator implementation. 潜在的问题是,对于每个装饰器实现,都将检查接口的代码协定。 I would like to potentially avoid that by only validating the contract once (preferably on the outermost decorator). 我希望通过仅一次验证合同(最好在最外面的装饰器上)来避免这种情况。

Is there something available out of the box to accomplish that? 有没有可用的现成的东西来实现这一目标? If not, what would be your suggestion to overcome this issue? 如果不是,您对解决此问题有何建议?

The generic interface and the contract class are like this: 通用接口和合同类如下所示:

[ContractClass(typeof(CommandHandlerContracts<>))]
public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

[ContractClassFor(typeof(ICommandHandler<>))]
internal abstract CommandHandlerContracts<TCommand>
    : ICommandHandler<TCommand>
{
    public void Handle(TCommand command)
    {
        Contract.Requires<ArgumentNullException>(
            command != null);
    }
}

And the ValidatorCommandHandler (as an example of how I'm implementing them) looks like this: ValidatorCommandHandler(作为我如何实现它们的示例)如下所示:

public sealed class ValidatorCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> m_decoratedHandler;
    private ICommandValidator<TCommand> m_commandValidator;

    public ValidatorCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler,
        ICommandValidator<TCommand> commandValidator)
    {
        m_decoratedHandler = decoratedHandler;
        m_commandValidator = commandValidator;
    }

    public void Handle(TCommand command)
    {
        m_commandValidator.Validate(command);
        m_decoratedHandler.Handle(command);
    }
}

Even if I created another interface to use just for the decorators, it would have to inherit the original interface and the contracts would still be checked, so I'm not sure how to go about this. 即使我创建了另一个仅用于装饰器的接口,它也必须继承原始接口,并且仍然要检查合同,因此我不确定该怎么做。

This is .NET 4.0 / VS2012 and I'm using the latest version of Code Contracts, if this helps any. 这是.NET 4.0 / VS2012,如果有帮助,我正在使用最新版本的代码合同。

You defined a contract on that interface, so Code Contracts will ensure the contract is met; 您在该接口上定义了合同,因此代码合同将确保合同得到遵守; a contract is a contract. 合同就是合同。 I guess you worry about performance, but this shouldn't be any problem from a performance perspective, since I expect the code that Code Contracts weaves in would be very fast (just a null check) in this case. 我猜您担心性能,但是从性能角度来看这应该不是什么问题,因为在这种情况下,我期望代码契约所编织的代码会非常快(只是空检查)。 You can verify this by using a IL decompiler. 您可以使用IL反编译器对此进行验证。

But if you really want to do this, the solution is actually really simple. 但是,如果您真的想这样做,则解决方案实际上非常简单。 When looking at what you're doing the question even seems a bit silly, since you should already know the answer: 当您查看正在执行的操作时,这个问题甚至显得有些愚蠢,因为您应该已经知道答案了:

Use a decorator! 使用装饰器!

Here is your new outer-most decorator: 这是您新的最外面的装饰器:

public class ContractCheckerCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratee;

    public ContractCheckerCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratee)
    {
        this.decoratee = decoratee;
    }

    public void Handle(TCommand command)
    {
        if (command == null)
        {
            throw new ArgumentNullException("command");
        }

        this.decoratee.Handle(command);
    }
}

Note however that when using Unity, the overhead of adding an extra decorator to do the null check, will be much greater than adding null checks to each and every implementation using Code Contracts. 但是请注意,当使用Unity时,添加额外的装饰器来执行空检查的开销将比使用代码协定对每个实现添加空检查的开销大得多。 On most cases, the overhead would probably still be neglectable when adding this extra decorator using Unity. 在大多数情况下,使用Unity添加此额外的装饰器时,开销可能仍然可以忽略不计。

If the preoccupation is about the contracts being verified on every Handle(ICommand) called. 如果关注点是要在每次调用的Handle(ICommand)上验证合同。 I´d say that´s okay since that´s what´s expected, since there isn´ta guarantee of what the wrapper is going to pass to the wrap-pee in a decorator chain, even if receive a non-null command. 我会说这是可以的,因为这是预期的,因为无法保证包装程序将传递给装饰器链中的包装小便,即使收到非空命令也是如此。

Once again I got a very interesting answer from Manuel Fahndrich in this thread over the Code Contracts devlabs forum. 在Code Contracts devlabs论坛上的这篇文章中,Manuel Fahndrich再次给了我一个非常有趣的答案。

There is an attribute that can be placed over members to setup specific contract behavior options, and this one is used to disable contract runtime checking on the handle method for instance: 可以在成员上放置一个属性,以设置特定的合同行为选项,例如,该属性用于禁用handle方法上的合同运行时检查:

[ContractOption("runtime", "checking", false)]
public void Handle(TCommand command)
{ ... }

As I said in the comments, I will keep the runtime checks in case some decorator decides do pass null to it's decorated class (completely possible). 正如我在评论中所说,我将保留运行时检查,以防某些装饰器决定将null传递给它的装饰类(完全可能)。 Since the overhead should be minimal, I ended up abandoning the initial idea. 由于开销应该很小,所以我最终放弃了最初的想法。 It is very nice to know that such a thing is possible though, and maybe this answer is useful to someone else when searching. 很高兴知道这样的事情是可能的,也许这个答案对其他人在搜索时很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Contract.Requires vs Contract.Require - Contract.Requires vs Contract.Require 证明此合同需要什么? - What does it take to prove this Contract.Requires? 简化的装饰图案。 那是对的吗? - Simplified decorator pattern. Is that correct? 点网核心如何处理Arg.NotNull和Contract.Requires资产 - How does dot net core process Arg.NotNull and Contract.Requires assets Contract.Requires在异常中ctor失败,错误CC1027:格式错误的合同 - Contract.Requires in exception ctor fails with error CC1027: Malformed contract 代码契约:我们是否必须在委托方法中冗余地指定Contract.Requires(...)语句? - Code Contracts: Do we have to specify Contract.Requires(…) statements redundantly in delegating methods? 在没有安装代码合同的情况下构建时,Contract.Requires会发生什么? - What happens to Contract.Requires when built without Code Contracts installed? 如何强制正则表达式匹配模式的最长部分。 - How do I force regex to match the longest possible part of the pattern. IDisposable模式。 我的终结者如何处置曾经免费的托管资源? - IDisposable Pattern. How does my finalizer's call of dispose ever free managed resources? 在WCF中避免消息合同 - Avoid Message Contract in WCF
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM