C# 转移IDisposable对象和生成器设计模式的所有权

C# 转移IDisposable对象和生成器设计模式的所有权,c#,.net,idisposable,C#,.net,Idisposable,我习惯于这样的方法:当对象直接或通过工厂、构建器等创建另一个对象时,它是“拥有”它的对象,从而管理它的生命周期 这个想法在几乎所有情况下都很有效 但有时创建对象的人根本无法管理它,例如在builder设计模式实现中: IFoo BuildFoo() { var dep = new Dep(); return new Foo(dep); } 因此,在这里,构建器无法管理dep对象的生存期,因为: 它没有提及它 它不知道何时可以安全地处置它 最简单的解决方案是使Foo:IDisp

我习惯于这样的方法:当对象直接或通过工厂、构建器等创建另一个对象时,它是“拥有”它的对象,从而管理它的生命周期

这个想法在几乎所有情况下都很有效

但有时创建对象的人根本无法管理它,例如在builder设计模式实现中:

IFoo BuildFoo()
{
    var dep = new Dep();

    return new Foo(dep);
}
因此,在这里,构建器无法管理
dep
对象的生存期,因为:

  • 它没有提及它
  • 它不知道何时可以安全地处置它
  • 最简单的解决方案是使
    Foo:IDisposable
    ,并让它管理传递给其构造函数的
    Dep

    但随后又出现了另一个难题:

    using (var dep = new Dep())
    {
        using (var foo = new Foo(dep))
        {
            // (1) do something with foo
        }
    
        // (2) !!! do something with dep !!!
    }
    
    上述代码变得不安全:此时
    (2)
    使用
    dep
    是不安全的,因为它已经被
    foo
    处理了

    在语法上,没有任何东西可以表示谁的责任是管理对象的生命周期


    那么问题是:什么是解决这个问题的通用解决方案呢?

    Foo
    调用构建器来实例化
    Dep
    ,并在需要时使用它。通过这种方式
    Foo
    管理
    Dep
    的生命周期,而
    Foo
    的客户机不必担心它。

    非常幼稚的实现

    void Main()
    {
        using (var builder = new Builder())
        {
            var foo = builder.Create();
            // do smtg
        }
    }
    
    public class Foo
    {
        public Foo(Dep instance)
        {
        }
    }
    
    public class Dep : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine($"{this.GetType().Name} disposed!");
        }
    }
    
    public class Builder : IDisposable
    {
        private List<IDisposable> _disposables = new List<System.IDisposable>();
    
        public Foo Create()
        {
            var dep = new Dep();
            _disposables.Add(dep);
    
            var foo = new Foo(dep);
            return foo;
        }
    
        public void Dispose()
        {
            foreach(var d in _disposables)
                d.Dispose();
    
            Console.WriteLine($"{this.GetType().Name} disposed!");
        }
    }
    
    void Main()
    {
    使用(var builder=new builder())
    {
    var foo=builder.Create();
    //做smtg吗
    }
    }
    公开课Foo
    {
    公共Foo(Dep实例)
    {
    }
    }
    公共类部门:IDisposable
    {
    公共空间处置()
    {
    Console.WriteLine($“{this.GetType().Name}disposed!”);
    }
    }
    公共类生成器:IDisposable
    {
    私有列表_disposables=新列表();
    公共Foo创建()
    {
    var dep=新的dep();
    _一次性用品。添加(dep);
    var foo=新foo(dep);
    返回foo;
    }
    公共空间处置()
    {
    foreach(不可处置资产中的var d)
    d、 处置();
    Console.WriteLine($“{this.GetType().Name}disposed!”);
    }
    }
    
    如果某个东西拥有IDisposable,那么它应该实现IDisposable。
    当然,这很简单,就像示例一样。

    在这种情况下,我不会担心它。我想我会在这里被击碎,但“建筑商”、“工厂”等都是我认为可以创建一个对象并将其处理交给要求它的对象的地方

    但有一条规则是,构建器/工厂必须只创建对象,而不对其进行任何操作


    当然,如果您使用
    new
    关键字更新对象,那么您将自己耦合到该实现(即使它是通过工厂类间接实现的)。您可能需要考虑依赖注入,这取决于所创建的对象是什么,在这种情况下,DI容器将根据它们配置的生活方式创建对象并在正确的时间为您处理这些对象。

    然后<代码> FoO 会知道构建器。控制反转的整个思想是,你被依赖所喂养。而且整个事情变得更难测试。那么,为什么你认为Foo的客户机需要管理Dep的生存期。为什么不让Foo在处理自己时处理Dep。因为没有明确的合同规定,在
    Foo
    处理后,其依赖项也处于无效状态。所以,然后让构建者管理Dep的生命周期,请参见@tym32167 answerp,这看起来更有趣。有没有一个成语说“任何由
    IDisposable
    对象产生的东西在处理后使用都不安全”?是的。关于这一点我读了很多次,但现在只记得《框架设计指南》一书。我想这本书会让你们感兴趣。另外,请看看你们是否在详细询问构建器模式,不确定是否有适合你们的答案。处理行为应该为生成器配置。例如,如果您使用的是DI容器,那么您可以选择哪些实例将由容器控制,哪些实例将由外部控制。>>这就是我的问题所在,没有确定的答案,因为这取决于项目中的情况。Dep是什么意思?如果Dep-somthing像stream一样,它应该由Foo处理,因为您可能不想使用它两次。但如果它类似于EventAggregator或DbTransaction,则不应该由Foo.Right处理,但请参见代码段#2。当一个对象是在这些创造模式之外创建的(这是完全合法的),那么就没有任何契约会暗示我们正在将我们传递的任何东西的所有权转移到它的构造函数中。保持一致。您应该只为难以创建的对象创建工厂(需要复杂的初始化等)。如果是,则拥有工厂并使用工厂。一致性有巨大的价值。构建器帮助构建复杂的对象(通常带有一些运行时条件),但其目的纯粹是辅助的。这与专为封装构造对象的唯一方法而设计的工厂略有矛盾。所以我不认为直接使用构建器和构造函数是不一致的。我不是这么说的。我的意思是,对于任何需要构建器的足够复杂的对象,拥有构建器并使用构建器。无论哪种方式,接收对象的代码都要对对象的可丢弃性负责。想想像
    new
    关键字这样的构建器和工厂——它们只是隐藏了您必须传递的所有依赖项。我会再次提到DI,因为我认为如果你在这个领域,这是一个合理的考虑。足够公平了。成语是“如果你创建了一个对象,你就要负责处理它”。