代码之家  ›  专栏  ›  技术社区  ›  Øyvind Bråthen

使用特定模式实现接口

  •  6
  • Øyvind Bråthen  · 技术社区  · 15 年前

    我最近对设计模式非常感兴趣,特别是在实现一个或多个接口的类中遵循正确的设计模式。

    IDisposable

    我还经常实现很多其他接口,但并不是所有的接口我都确定我实现它们的方式是否是首选的方式,我可能会在以后发现,它会导致一个很难找到的微妙的错误,如果我在第一时间遵循正确的模式,这可能是不存在的。

    我想知道的一些接口的最佳实现方式是ISerializable、IComparable、IComparable<、ICloneable、IEnumerable<,等等。框架中的所有接口在这里都很有趣,因此不应局限于我上面列出的那些接口。

    我希望获得这些模式的良好集合,因为我知道它们可以极大地改进我的代码,使其更加正确,并遵循最佳实践

    如果同一个接口有几种模式,那就太好了,这样我们就可以讨论哪种模式更可取。这也可能会使一些人可能会转向新的模式,或者对现有模式进行修改,以进一步改进代码,这太好了!

    编辑

    在阅读了Grzenios的评论之后,我还要敦促大家给出应该应用的模式的上下文。例如,只有在类中有一些非托管资源需要释放时才应该遵循IDIsposable模式,而不是在所有需要释放的对象都实现了IDIsposable本身时才应该遵循IDIsposable模式。

    编辑2

    只有当类中包含一个或多个非托管资源,并且必须确保它们被释放时,才应该使用此模式。在这种情况下,除了Dispose方法之外,我们还需要一个终结器,以防您的类的用户忘记处理它。

    所以第一件事是。您的类应该实现IDisposable接口,并且必须将public Dispose方法定义为由接口管理。此方法应如下所示:

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }
    

    private bool alreadyDisposed = false;
    

    气相色谱仪告诉垃圾收集器,即使此项有终结器,也不需要终结。

    然后需要受保护的Dispose方法。使其受保护而不是私有,以防任何派生类需要重写它:

    protected virtual void Dispose(bool isDisposing)
    {
      if (alreadyDisposed)
      {
        return;
      }
      if (isDisposing)
      {
        // free all managed resources here
      }
      // free all unmanaged resources here.
      alreadyDisposed = true;
    }
    

    ~SomeClass(){
      Dispose(false);
    }
    

    public void SomeMethod()
    {
      if (alreadyDisposed)
        throw new ObjectDisposedException("SomeClass",
                                          "Called SomeMethod on Disposed object");
      // Method body goes here
    }
    

    就这样。这将确保资源得到清理。最好由调用Dispose的类的用户来执行,但要添加终结器作为回退方法。

    2 回复  |  直到 15 年前
        1
  •  3
  •   Mark H    15 年前

    在学习设计模式的同时,还应该了解一些常见的反模式,从而了解模式的来源。 IDisposable 有点反模式,是 sequential coupling ,因为它需要用户调用dispose,如果他忘记了,你就完了。“一次性模式”的主要原因之一就是为了解决这个问题。

    在可能的情况下,我的首选技术不是向用户公开IDisposable对象,而是公开单个方法(调用它) Using(...) using(...) { } 阻止。这个方法可以执行您的构造,执行委托,然后处理它所消耗的资源,并且省略了IDisposable的至少3个问题:用户忘记调用它,用户多次调用它,以及用户过早调用它—在没有IDisposable的情况下,您不需要为样板文件“一次性模式”而烦恼可识别暴露。

    例如,假设我有一个文件IO需求,需要定期打开相同的文件(因此,如果用户忘记调用Dispose,我不能等待垃圾收集器调用Finalize)。

    class MyFileStream {
        FileStream fs;
        private MyFileStream(string filename, FileMode mode) {
            fs = new FileStream(filename, FileMode.Open);
        }
        private void Dispose() {
            fs.Dispose();
        }
        public static void Using(string filename, FileMode mode, Action<MyFileStream> use) {
            MyFileStream mfs = new MyFileStream(filename, mode);
            use(mfs);
            mfs.Dispose();
        }
        public void Read(...) { ... }
    }
    

    打电话的人可以说

    var x = default(...);
    MyFileStream.Using("filename.txt", FileMode.Open, (stream) => {
        x = stream.Read(...);
    });
    Console.WriteLine(x);
    

    请注意,这与 using() { }

    尽管如此,这种模式并不总是合适的,因为有时您需要的资源要比仅仅一个方法调用持续更长的时间,但是如果您发现有机会使用它,就这样做。

    反正我已经完全偏离主题了,回到你刚才问的问题上来。

    • 对于IEnumerable<,很少需要创建自己的类,因为框架中已经有相当大的现有类集合,而且通常可以通过实现扩展方法(例如LINQ中的扩展方法,或者您自己的扩展方法,利用强大的 yield

    我不会说剩下的有什么特别的模式,它们是不言自明的。

        2
  •  0
  •   atconway    15 年前

    例如,只有在类中有一些非托管资源需要释放时才应该遵循IDIsposable模式,而不是在所有需要释放的对象都实现了IDIsposable本身时才应该遵循IDIsposable模式。

    我不同意上面的说法。我从不建议允许GC进行清理并隐式地处理对象和资源。这有点像在酒店等女佣过来帮你拿湿毛巾;这终究会发生,但正确的做法是自己拿起来挂起来。

    确定性地处理对象并将这些资源的作用域保持在尽可能小的范围内,将有助于实现最精简和最高效的应用程序,更不用说对于阅读代码的开发人员来说,更明确地处理资源和更好地阅读代码。就像一个故事的开始和结束,一个人可以在一个地方看到一切。我尽可能晚地实例化对象,并在使用后尽快处理它。对对象显式调用.Dispose或使用自动调用.Dispose方法的Using块是(2)清理的好方法。

    我还经常实现很多其他接口,但并不是所有的接口我都确定我实现它们的方式是否是首选的方式,我可能会在以后发现,它会导致一个很难找到的微妙的错误,如果我在第一时间遵循正确的模式,这可能是不存在的。

    接口的全部目的是为类要实现的方法、属性、事件等创建一个guidline,但不提供如何实现的细节。那由你决定。如何实现specefic接口本身没有“模式”。不要被IDisposable接口抛出。在中创建接口时与.NET,将为您创建丰满的代码 典型的界面,在现实中可以完全改变你所需要的。

    我认为您可能会对实现.NET框架中的接口感到困惑,但您需要了解接口的概念,而不仅仅是框架中可用的接口。如果您真的想查看.NET Framework中的接口是如何实现的示例,请查看MSDN或反编译实现这些接口的其他Framework对象,以了解Microsoft是如何实现这些接口的。

    所以你不能只看.NET框架中的接口,以及如何将某种模式应用到它们的实现中。实现细节、模式等由实现类决定,只要接口的细节足够,那么这就是问题所在。

    希望这有点帮助。。。

    推荐文章