如果实现接口调用Dispose,它是否是一个漏洞抽象

时间:2012-09-04 08:26:45

标签: c# .net design-patterns dependency-injection loose-coupling

考虑以下代码:

public class MyClass()
{
  public MyClass()
  {    
  }

  public DoSomething()
  {
    using (var service = new CustomerCreditServiceClient())
    {
       var creditLimit = service.GetCreditLimit(
         customer.Firstname, customer.Surname, customer.DateOfBirth);       
    }
  }
}

我们现在想重构它以松散地耦合它。我们最终得到了这个:

public class MyClass()
{
  private readonly ICustomerCreditService service;

  public MyClass(ICustomerCreditService service)
  {
     this.service= service;
  }

  public DoSomething()
  {
     var creditLimit = service.GetCreditLimit(
       customer.Firstname, customer.Surname, customer.DateOfBirth);       
  }
}

看起来不错吧?现在任何实现都可以使用该接口,一切都很好。

如果我现在说实现是一个WCF类并且重构之前的using语句是有原因的,那该怎么办? ie /关闭WCF连接。

所以现在我们的接口必须实现Dispose方法调用,或者我们使用工厂接口来获取实现并在其周围放置一个using语句。

对我来说(尽管这个主题是新的),这似乎是一个漏洞的抽象。我们不得不在代码中添加方法调用,只是为了实现处理内容的方式。

有人可以帮助我理解这一点,并确认我是对还是错。

由于

5 个答案:

答案 0 :(得分:5)

是的,当您ICustomerCreditService实施IDisposable时,这是一个漏洞抽象,因为您现在已经考虑了ICustomerCreditService并考虑了具体的实现。此外,这向该接口的消费者传达它可以处置该服务,这可能是不正确的,特别是因为通常,资源应该由创建它的人(具有所有权)处理。当您将资源注入类(例如使用构造函数注入)时,不清楚是否给予使用者所有权。

因此,一般来说,负责创建该资源的人应该将其处理掉。

但是,在您的情况下,您可以通过实现ICustomerCreditServiceClient的非一次性实现来简单地防止这种情况发生,该实现仅在同一方法调用中创建和处置WCF客户端。这使一切变得更加容易:

public class WcfCustomerCreditServiceClient
    : ICustomerCreditServiceClient
{
    public CreditLimit GetCreditLimit(Customer customer)
    {
        using (var service = new CustomerCreditServiceClient())
        {
            return service.GetCreditLimit(customer.Firstname,
                customer.Surname, customer.DateOfBirth);       
        }
    }
}

答案 1 :(得分:1)

您应该在调用代码中处理customerCreditService的生命周期。 MyClass应该如何知道调用者是否还需要该服务?如果调用者负责清理其资源,则MyClass不需要是一次性的。

// calling method

using (var service = new CustomerCreditServiceClient()) {
    var myClass = new MyClass(service);
    myClass.DoSomething();
}

更新:在评论中,OP提到了使用像Ninject这样的IoC。然后代码可能如下所示:

IKernel kernel = ...;

using (var block = kernel.BeginBlock())
{
    var service = block.Get<ICustomerCreditService>();
    var myClass = new MyClass(service);
    myClass.DoSomething();
}

kernel.BeginBlock()会创建一个激活块。它确保在块结束时处理已解析的实例。

答案 2 :(得分:1)

你应该调用ICustomerCreditService已经实例化的MyClass的Dispose,因为ICustomerCreditService现在不知道{{1}}的生命周期。

答案 3 :(得分:1)

从第一个实现开始,我将尝试向类添加一个getInterface-Request,以便实现可以保持或多或少相同。然后它可以安全地调用Dispose(实际上它只推迟了接口实现的创建,但仍然控制着它的生命周期): (c#-code not verified ...)

public class MyClass()
{
  public delegate ICustomerCreditService InterfaceGetter;
  private InterfceGetter getInterface;
  public MyClass(InterfaceGetter iget)
  {
    getInterface = iget;
  }
  public DoSomething()
  {
    using (var customerCreditService = getInterface())
    {
       var creditLimit = customerCreditService.GetCreditLimit(customer.Firstname, customer.Surname, customer.DateOfBirth);       
    }
  }
}

答案 4 :(得分:0)

是的,确实如此。但这是一个必要的邪恶。 IDisposable接口的存在是一个漏洞抽象。漏洞抽象只是编程的日常事实。尽可能避免使用它们,但不要担心 - 无论如何它们无处不在。