简体   繁体   English

C# 在继承的构造函数中初始化 Disposable

[英]C# Initialize Disposable in inherited constructor

Hey so I have a base class coming from a 3rd party dll, which is dependent on a disposable.嘿,所以我有一个来自第 3 方 dll 的基础 class,它依赖于一次性用品。 Context: IDisposable

public class BaseValidator
{
    public BaseValidator(Context context) {}
}

We're trying to move away from tying our classes to these dependencies.我们正试图摆脱将我们的类与这些依赖项绑定在一起。 So we started relying on providers instead所以我们开始依赖供应商

public interface IContextProvider 
{
    Context Create();
}

I have a new validator that I'm writing which inherits from the BaseValidator, but I would like it to be dependent on the IContextProvider instead.我正在编写一个新的验证器,它继承自 BaseValidator,但我希望它依赖于IContextProvider So I'd like to create the context in the inherited constructor, but I would like to dispose of it in the destructor to prevent memory leaks, However I'm not sure if this is possible.所以我想在继承的构造函数中创建上下文,但我想在析构函数中处理它以防止 memory 泄漏,但是我不确定这是否可能。

public class EntityValidator: BaseValidator 
{
    public EntityValidator(IContextProvider provider) : base(provider.Create()) 
    {
    }

    ~EntityValidator()
    {
        //I'm not how I can dispose the entity I've passed into it.
    }
}

My question is, is there a trick I can use to Capture the variable passed into the base?我的问题是,我可以使用一个技巧来捕获传递到基础的变量吗?

Note: I know I can make a work around with an external helper class, but I'm interested if anyone knows how to do this in a more savvy way.注意:我知道我可以使用外部助手 class 来解决问题,但如果有人知道如何以更精明的方式做到这一点,我很感兴趣。

If the BaseValidator class does not expose Context in a public manner, your current design would require you use reflection and knowledge of the internal implementation of BaseValidator to dispose of it, which is of course fragile.如果BaseValidator class 没有以公共方式公开Context ,那么您当前的设计将需要您使用反射和BaseValidator内部实现的知识来处理它,这当然是脆弱的。

I would instead capture the context using an intermediate constructor:我会改为使用中间构造函数捕获上下文:

Context _context;
private EntityValidator(Context context) : base(context) 
{ 
    _context = context;
}
public EntityValidator(IContextProvider provider) : this(provider.Create())
{
    
}

Note, disposing via a finalizer (aka destructor) is not ideal due to constraints it places on the garbage collector.请注意,通过终结器(又名析构函数)进行处理并不理想,因为它对垃圾收集器施加了限制 I'd instead have EntityValidator implement IDisposable我会改为让EntityValidator实现IDisposable

Before, in other language like C++, developpers used to rely on destructors to do a lot of cleaning.以前,在 C++ 等其他语言中,开发人员过去常常依靠析构函数进行大量清理。 It was guaranteed by compiler so it was a strong behavior.它是由编译器保证的,因此它是一种强大的行为。 Smart pointer was a good pattern that used this behavior (to implement something like very basic but automatic garbage collection system).智能指针是一个很好的模式,它使用了这种行为(实现一些非常基本但自动的垃圾收集系统)。 Code was elegant but people used it for a lot of other need.代码很优雅,但人们将它用于许多其他需求。 With time a lot of code used to happen in destructor making the debug and readability hard.随着时间的推移,析构函数中经常发生大量代码,使得调试和可读性变得困难。

IDisposable has been made to force developpers to write explicitely the call to Dispose... This is also useful when you want to Dispose internal resource without your object being destructed. IDisposable 已强制开发人员明确编写对 Dispose 的调用...当您想要在不破坏 object 的情况下处理内部资源时,这也很有用。 For example in some "Close" method of Stream, where the stream is definitively closed but you can still have reference on steam... and use IsOpen property...例如,在 Stream 的某些“关闭”方法中,stream 已明确关闭,但您仍然可以在 steam 上有参考...并使用 IsOpen 属性...

So for me you should not try to hide it.所以对我来说,你不应该试图隐藏它。 If you depends on code that needs to be Disposed, embrace this dependency... or chose another third party library.如果您依赖于需要处理的代码,请接受此依赖项......或选择另一个第三方库。 You can simply make the class that need to manipulate Disposable object (BaseValidator) IDisposable too and delegate the need to call to the user.您也可以简单地将需要操作 Disposable object (BaseValidator) IDisposable 的 class 设置为 IDisposable 并将调用的需要委托给用户。

Usually people that write class implementing IDisposable, in open source project, also write destructor (just in case someone forgot to call Dispose) This is true for a lot of class of.Net framework too (for example Pen() { where nobody thinks to call Dispose on it in some Control drawing events...)通常写 class 实现 IDisposable 的人,在开源项目中,也写析构函数(以防万一有人忘记调用 Dispose)对于很多 class 的 .Net 框架也是如此(例如 Pen() { 没有人认为在一些控件绘图事件中调用 Dispose ......)

So I would recommend:所以我会推荐:

  1. Go get the information about if they did it with their classes, if yes they are strong probability they will keep this behavior/code forever. Go 获取有关他们是否使用他们的类执行此操作的信息,如果是,他们很有可能将永远保留此行为/代码。 So you can just make your class to inherit IDisposable too, call context.Dispose in your own Dispose implement and that's enough... no need to worry because if your user forgot to call Dispose, the third party do the cleaning.所以你可以让你的 class 也继承 IDisposable,在你自己的 Dispose 工具中调用 context.Dispose 就足够了......不用担心,因为如果你的用户忘记调用 Dispose,第三方会进行清理。 Add a destructor/finalizer on your class only if you have other unmanaged resource to clean too...仅当您还有其他非托管资源要清理时,才在 class 上添加析构函数/终结器...

  2. Now if they did not, you can just wrap third party "Context" class in your own Context class.现在,如果他们没有,您可以将第三方“上下文”class 包装在您自己的上下文 class 中。 Your Context class will have to implement destructor that call Dispose on the instance of third party Context class.您的上下文 class 将必须在第三方上下文 class 的实例上实现调用 Dispose 的析构函数。 And that's it.就是这样。 You can even make your Context class sealed.您甚至可以将 Context class 密封。 Your Context class is here to make the behavior of your app as close as if 1) was true and implemented by third party.您的上下文 class 在这里使您的应用程序的行为尽可能接近 1) 是真实的并由第三方实施。 So this class will be easy to remove later because third party will probably implement finalizer one day (if they are serious).所以这个 class 稍后将很容易删除,因为第三方可能有一天会实施终结器(如果他们是认真的)。 Doing just a sealed wrapper around just one class will avoid some complexity/issue related to how destructors (finallizers) works in.Net: They are all called in any order.仅围绕一个 class 进行密封包装将避免与析构函数(终结器)在.Net 中的工作方式相关的一些复杂性/问题:它们都以任何顺序调用。 Because of this underterministic behavior it makes your code hard to maintain later.由于这种不确定的行为,它使您的代码以后难以维护。 For example if third party Context class's finalizer is called before your wrapping class => exception can occured and at a bad time (when gc is doing its stuff) which can make your app crash.例如,如果在包装 class => 之前调用第三方 Context 类的终结器 => 可能会发生异常并且在错误的时间(当 gc 正在执行它的工作时),这可能会使您的应用程序崩溃。 Because of all these problems, you better go back to 1)由于所有这些问题,您最好 go 回到 1)

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM