pt622光猫切换模式_一次性模式(一次性设计原则)pt.3

pt622光猫切换模式

多线程 (Multithreading)

Now let’s talk about thin ice. In the previous sections about IDisposable we touched one very important concept that underlies not only the design principles of Disposable types but any type in general. This is the object’s integrity concept. It means that at any given moment of time an object is in a strictly determined state and any action with this object turns its state into one of the variants that were pre-determined while designing a type of this object. In other words, no action with the object should turn it into an undefined state. This results in a problem with the types designed in the above examples. They are not thread-safe. There is a chance the public methods of these types will be called when the destruction of an object is in progress. Let’s solve this problem and decide whether we should solve it at all.

现在让我们谈谈薄冰。 在前面有关IDisposable的部分中,我们触及了一个非常重要的概念,该概念不仅是Disposable类型的设计原理的基础,而且还是一般类型的基础。 这是对象的完整性概念。 这意味着在任何给定的时间,对象都处于严格确定的状态,并且对该对象的任何操作都将其状态转换为在设计此对象的类型时预先确定的变体之一。 换句话说,对对象的任何操作都不应将其变成未定义状态。 这导致上述示例中设计的类型出现问题。 它们不是线程安全的。 当销毁对象时,可能会调用这些类型的公共方法。 让我们解决这个问题并决定是否应该彻底解决它。

This chapter was translated from Russian jointly by author and by
professional translators. You can help us with translation from Russian or English into any other language, primarily into Chinese or German.
Also, if you want thank us, the best way you can do that is to give us a star on github or to fork repository .
本章由作者和
专业翻译员共同译自俄语。 您可以帮助我们将俄语或英语翻译成任何其他语言,主要是中文或德语。
另外,如果您想感谢我们,最好的方法是在github上给我们加星号或分支存储库 github / sidristij / dotnetbook
public class FileWrapper : IDisposable
{
    IntPtr _handle;
    bool _disposed;
    object _disposingSync = new object();

    public FileWrapper(string name)
    {
        _handle = CreateFile(name, 0, 0, 0, 0, 0, IntPtr.Zero);
    }

    public void Seek(int position)
    {
        lock(_disposingSync)
        {
            CheckDisposed();
            // Seek API call
        }
    }

    public void Dispose()
    {
        lock(_disposingSync)
        {
            if(_disposed) return;
            _disposed = true;
        }
        InternalDispose();
        GC.SuppressFinalize(this);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void CheckDisposed()
    {
        lock(_disposingSync)
        {
            if(_disposed) {
                throw new ObjectDisposedException();
            }
        }
    }

    private void InternalDispose()
    {
        CloseHandle(_handle);
    }

    ~FileWrapper()
    {
        InternalDispose();
    }

    /// other methods
}

The _disposed validation code in Dispose() should be initialized as a critical section. In fact, the whole code of public methods should be initialized as a critical section. This will solve the problem of concurrent access to a public method of an instance type and to a method of its destruction. However, it brings other problems that become a timebomb:

Dispose()中的_disposed验证代码应初始化为关键部分。 实际上,应该将公共方法的整个代码初始化为关键部分。 这将解决并发访问实例类型的公共方法及其销毁方法的问题。 但是,它带来了其他问题,成为了定时炸弹:

  • The intensive use of type instance methods as well as the creation and destruction of objects will lower the performance significantly. This is because taking a lock consumes time. This time is necessary to allocate SyncBlockIndex tables, check current thread and many other things (we will deal with them in the chapter about multithreading). That means we will have to sacrifice the object’s performance throughout its lifetime for the “last mile” of its life.

    大量使用类型实例方法以及对象的创建和销毁将大大降低性能。 这是因为进行锁定会浪费时间。 这是分配SyncBlockIndex表,检查当前线程和许多其他事情的必要时间(我们将在有关多线程的章节中进行介绍)。 这意味着我们将不得不在生命的“最后一英里”内牺牲其生命周期内的性能。
  • Additional memory traffic for synchronization objects.

    同步对象的其他内存流量。
  • Additional steps GC should take to go through an object graph.

    GC应该采取其他步骤来遍历对象图。

Now, let’s name the second and, in my opinion, the most important thing. We allow the destruction of an object and at the same time expect to work with it again. What do we hope for in this situation? that it will fail? Because if Dispose runs first, then the following use of object methods will definitely result in ObjectDisposedException. So, you should delegate the synchronization between Dispose() calls and other public methods of a type to the service side, i.e. to the code that created the instance of FileWrapper class. It is because only the creating side knows what it will do with an instance of a class and when to destroy it. On the other hand, a Dispose call should produce only critical errors, such as OutOfMemoryException, but not IOException for example. This is because of the requirements for the architecture of classes that implement IDisposable. It means that if Dispose is called from more than one thread at a time, the destruction of an entity may happen from two threads simultaneously (we skip the check of if(_disposed) return;). It depends on the situation: if a resource can be released several times, there is no need in additional checks. Otherwise, protection is necessary:

现在,让我们命名第二个,我认为是最重要的。 我们允许销毁对象,同时希望再次使用它。 在这种情况下,我们希望什么? 它会失败吗? 因为如果首先运行Dispose,则随后使用对象方法肯定会导致ObjectDisposedException 。 因此,您应该将Dispose()调用与其他类型的公共方法之间的同步委托给服务端,即FileWrapper给创建FileWrapper类实例的FileWrapper 。 这是因为只有创建方才知道它将对一个类的实例执行什么操作以及何时销毁它。 另一方面,Dispose调用仅应产生严重错误,例如OutOfMemoryException ,而不是IOException。 这是因为实现IDisposable的类的体系结构要求。 这意味着,如果一次从多个线程中调用Dispose,则可能同时从两个线程中破坏一个实体(我们跳过对if(_disposed) return;的检查)。 这取决于情况:如果可以多次释放资源,则无需进行其他检查。 否则,必须进行保护:

// I don’t show the whole pattern on purpose as the example will be too long
// and will not show the essence
class Disposable : IDisposable
{
    private volatile int _disposed;

    public void Dispose()
    {
        if(Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
        {
            // dispose
        }
    }
}

一次性设计原则的两个层次 (Two levels of Disposable Design Principle)

What is the most popular pattern to implement IDisposable that you can meet in .NET books and the Internet? What pattern is expected from you during interviews for a potential new job? Most probably this one:

在.NET书籍和Internet中可以实现的最受欢迎的实现IDisposable模式是什么? 面试期间您期望您找到什么样的新工作方式? 最可能的是:

public class Disposable : IDisposable
{
    bool _disposed;

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

    protected virtual void Dispose(bool disposing)
    {
        if(disposing)
        {
            // here we release managed resources
        }
        // here we release unmanaged resources
    }

    protected void CheckDisposed()
    {
        if(_disposed)
        {
            throw new ObjectDisposedException();
        }
    }

    ~Disposable()
    {
        Dispose(false);
    }
}

What is wrong with this example and why we haven’t written like this before? In fact, this is a good pattern suitable for all situations. However, its ubiquitous use is not a good style in my opinion as we almost don’t deal with unmanaged resources in practice that makes half of the pattern serve no purpose. Moreover, since it simultaneously manages both managed and unmanaged resources, it violates the principle of responsibility division. I think this is wrong. Let’s look at a slightly different approach. Disposable Design Principle. In brief, it works as follows:

这个例子有什么问题,为什么我们以前没有这样写? 实际上,这是适用于所有情况的良好模式。 但是,在我看来,无处不在使用它不是一种好的样式,因为在实践中我们几乎不处理非托管资源,这使得一半的模式毫无用处。 而且,由于它同时管理托管和非托管资源,因此违反了责任划分的原则。 我认为这是错误的。 让我们看一个稍微不同的方法。 一次性设计原则 。 简而言之,它的工作方式如下:

Disposing is divided into two levels of classes:

处置分为两个级别的类:

  • Level 0 types directly encapsulate unmanaged resources

    0级类型直接封装非托管资源

    • They are either abstract or packed.

      它们是抽象的或打包的。
    • All methods should be marked:

      所有方法都应标记:

      – PrePrepareMethod, so that a method could be compiled when loading a type

      – PrePrepareMethod,以便在加载类型时可以编译方法

      • SecuritySafeCritical to protect against a call from the code, working under restrictions

        SecuritySafeCritical可以防止代码调用,在限制下工作
      • ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success / MayFail)] to put CER for a method and all its child calls

        ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success / MayFail)]将CER放入方法及其所有子调用中

        – They can reference Level 0 types, but should increment the counter of referencing objects to guarantee the right order of entering the “last mile”

        –它们可以引用0级类型,但应增加引用对象的计数器,以确保输入“最后一英里”的正确顺序

  • Level 1 types encapsulate only managed resources

    级别1类型仅封装托管资源

    • They are inherited only from Level 1 types or directly implement IDisposable

      它们仅从1级类型继承,或直接实现IDisposable
    • They cannot inherit Level 0 types or CriticalFinalizerObject

      他们不能继承0级类型或CriticalFinalizerObject
    • They can encapsulate Level 1 and Level 0 managed types

      它们可以封装1级和0级托管类型
    • They implement IDisposable.Dispose by destroying encapsulated objects starting from Level 0 types and going to Level 1

      它们实现IDisposable.Dispose通过销毁从Level 0类型开始到Level 1的封装对象进行处理
    • They don’t implement a finalizer as they don’t deal with unmanaged resources

      他们不实现终结器,因为它们不处理非托管资源
    • They should contain a protected property that gives access to Level 0 types.

      它们应包含一个受保护的属性,该属性可以访问0级类型。

That is why I used the division into two types from the beginning: the one that contains a managed resource and the one with unmanaged resource. They should function differently.

这就是为什么我从一开始就将划分使用两种类型的原因:一种包含托管资源,另一种包含非托管资源。 它们的功能应有所不同。

其他使用Dispose的方式 (Other ways to use Dispose)

The idea behind the creation of IDisposable was to release unmanaged resources. But as with many other patterns it is very helpful for other tasks, e.g. to release references to managed resources. Though releasing managed resources doesn’t sound very helpful. I mean they are called managed on purpose so we would relax with a grin regarding C/C++ developers, right? However, it is not so. There always may be a situation where we lose a reference to an object but at the same time think that everything is OK: GC will collect garbage, including our object. However, it turns out that memory grows. We get into the memory analysis program and see that something else holds this object. The thing is that there can be a logic for implicit capture of a reference to your entity in both .NET platform and the architecture of external classes. As the capture is implicit, a programmer can miss the necessity of its release and then get a memory leak.

创建IDisposable背后的想法是释放非托管资源。 但是,与其他许多模式一样,它对其他任务也非常有用,例如,释放对托管资源的引用。 尽管释放托管资源听起来不是很有帮助。 我的意思是说它们被故意地称为托管的,所以我们会对C / C ++开发人员笑容满面,对吧? 但是,事实并非如此。 在某些情况下,我们可能会丢失对某个对象的引用,但同时又认为一切正常:GC将收集包括我们的对象在内的垃圾。 但是,事实证明,内存在增长。 我们进入内存分析程序,发现还有其他东西可以容纳这个对象。 事实是,在.NET平台和外部类的体系结构中,都可能存在隐式捕获对实体的引用的逻辑。 由于捕获是隐式的,因此程序员可能会错过释放它的必要性,然后导致内存泄漏。

代表,活动 (Delegates, events)

Let’s look at this synthetic example:

让我们看一下这个综合示例:

class Secondary
{
    Action _action;

    void SaveForUseInFuture(Action action)
    {
        _action = action;
    }

    public void CallAction()
    {
        _action();
    }
}

class Primary
{
    Secondary _foo = new Secondary();

    public void PlanSayHello()
    {
        _foo.SaveForUseInFuture(Strategy);
    }

    public void SayHello()
    {
        _foo.CallAction();
    }

    void Strategy()
    {
        Console.WriteLine("Hello!");
    }
}

Which problem does this code show? Secondary class stores Action type delegate in _action field that is accepted in SaveForUseInFuture method. Next, PlanSayHello method inside Primary class passes pointer to Strategy method to Secondary class. It is curious but if, in this example, you pass somewhere a static method or an instance method, the passed SaveForUseInFuture will not be changed, but a Primary class instance will be referenced implicitly or not referenced at all. Outwardly it looks like you instructed which method to call. But in fact, a delegate is built not only using a method pointer but also using the pointer to an instance of a class. A calling party should understand for which instance of a class it has to call the Strategy method! That is the instance of Secondary class has implicitly accepted and holds the pointer to the instance of Primary class, though it is not indicated explicitly. For us it means only that if we pass _foo pointer somewhere else and lose the reference to Primary, then GC will not collect Primary object, as Secondary will hold it. How can we avoid such situations? We need a determined approach to release a reference to us. A mechanism that perfectly fits this purpose is IDisposable

此代码显示哪个问题? 辅助类将Action类型委托存储在SaveForUseInFuture方法接受的_action字段中。 接下来, Primary类中的PlanSayHello方法将指向Strategy方法的指针传递给Secondary类。 很好奇,但是,在此示例中,如果您在某个地方传递了静态方法或实例方法,则传递的SaveForUseInFuture将不会更改,但是会隐式引用或根本不会引用Primary类实例。 从外观上看,您已指示要调用的方法。 但是实际上,不仅使用方法指针构建委托,而且使用指向类实例的指针构建委托。 调用方应该了解必须为类的哪个实例调用Strategy方法! 这是Secondary类的实例已隐式接受,并保存指向Primary类的实例的指针,尽管未明确指出。 对我们来说,这仅意味着如果我们在其他地方传递_foo指针并丢失对Primary的引用,则GC 将不会收集 Primary对象,因为Secondary将保留它。 我们如何避免这种情况? 我们需要一种确定的方法来发布对我们的引用。 完全适合此目的的机制是IDisposable

// This is a simplified implementation
class Secondary : IDisposable
{
    Action _action;

    public event Action<Secondary> OnDisposed;

    public void SaveForUseInFuture(Action action)
    {
        _action = action;
    }

    public void CallAction()
    {
        _action?.Invoke();
    }

    void Dispose()
    {
        _action = null;
        OnDisposed?.Invoke(this);
    }
}

Now the example looks acceptable. If an instance of a class is passed to a third party and the reference to _action delegate will be lost during this process, we will set it to zero and the third party will be notified about the destruction of the instance and delete the reference to it. The second danger of code that runs on delegates is the functioning principles of event. Let’s look what they result in:

现在该示例看起来可以接受。 如果将某个类的实例传递给第三方,并且在此过程中对_action委托的引用将丢失,我们将其设置为零,并且将通知第三方有关实例销毁的信息并删除对该实例的引用。 在委托上运行的代码的第二个危险是event的起作用原理。 让我们看看它们的结果:

// a private field of a handler
private Action<Secondary> _event;

// add/remove methods are marked as [MethodImpl(MethodImplOptions.Synchronized)]
// that is similar to lock(this)
public event Action<Secondary> OnDisposed {
    add { lock(this) { _event += value; } }
    remove { lock(this) { _event -= value; } }
}

C# messaging hides the internals of events and holds all the objects that subscribed to update through event. If something goes wrong, a reference to a signed object remains in OnDisposed and will hold the object. It is a strange situation as in terms of architecture we get a concept of “events source” that shouldn’t hold anything logically. But in fact, objects subscribed to update are held implicitly. In addition, we cannot change something inside this array of delegates though the entity belongs to us. The only thing we can do is to delete this list by assigning null to an events source.

C#消息传递隐藏了事件的内部,并保留了所有通过event订阅进行更新的对象。 如果出现问题,对已签名对象的引用将保留在OnDisposed ,并将保留该对象。 这是一个奇怪的情况,因为在体系结构方面,我们得到了“事件源”的概念,该概念不应在逻辑上持有任何东西。 但是实际上,订阅更新的对象是隐式持有的。 此外,尽管实体属于我们,但我们无法在此委托数组中进行任何更改。 我们唯一可以做的就是通过将null分配给事件源来删除此列表。

The second way is to implement add/remove methods explicitly, so we could control a collection of delegates.

第二种方法是显式实现add / remove方法,因此我们可以控制委托的集合。

NullReferenceException. I think this would be more logical. NullReferenceException 。 我认为这会更合逻辑。

However, this is not true. If external code subscribes to events after an events source is cleared, FCL will create a new instance of Action class and store it in OnDisposed. This implicitness in C# can mislead a programmer: dealing with nulled fields should produce a sort of alertness rather than calmness. Here we also demonstrate an approach when the carelessness of a programmer can lead to memory leaks.

但是,事实并非如此。 如果在清除事件源后外部代码订阅了事件,则FCL将创建Action类的新实例并将其存储在OnDisposed 。 C#中的这种隐式性可能会误导程序员:处理空字段应该产生一种警惕而不是平静。 在这里,我们还演示了当程序员的粗心大意可能导致内存泄漏时的一种方法。

Lambda,关闭 (Lambdas, closures)

Using such syntactic sugar as lambdas is especially dangerous.

使用lambdas这样的语法糖特别危险。

I would like to touch upon syntactic sugar as a whole. I think you should use it rather carefully and only if you know the outcome exactly. Examples with lambda expressions are closures, closures in Expressions and many other miseries you can inflict upon yourself.
我想整体谈谈语法糖。 我认为您应该相当谨慎地使用它,并且只有在您完全知道结果的情况下。 使用lambda表达式的示例包括闭包,表达式中的闭包以及您可能对自己施加的许多其他麻烦。

Of course, you may say you know that a lambda expression creates a closure and can result in a risk of resource leak. But it is so neat, so pleasant that it is hard to avoid using lambda instead of allocating the entire method, that will be described in a place different from where it will be used. In fact, you shouldn’t buy into this provocation, though not everybody can resist. Let’s look at the example:

当然,您可能会说您知道lambda表达式会创建一个闭包,并可能导致资源泄漏的风险。 但这是如此整洁,令人愉快,以至于很难避免使用lambda而不是分配整个方法,这将在与使用方法不同的地方进行描述。 实际上,尽管不是每个人都可以抗拒,但您不应接受这种挑衅。 让我们看一个例子:

button.Clicked += () => service.SendMessageAsync(MessageType.Deploy);

Agree, this line looks very safe. But it hides a big problem: now button variable implicitly references service and holds it. Even if we decide that we don’t need service anymore, button will still hold the reference while this variable is alive. One of the ways to solve this problem is to use a pattern for creating IDisposable from any Action (System.Reactive.Disposables):

同意,此行看起来非常安全。 但这隐藏了一个大问题:现在button变量隐式引用了service并保留了它。 即使我们决定不再需要service ,在此变量处于活动状态时, button仍将保留引用。 解决此问题的方法之一是使用一种模式通过任何Action ( System.Reactive.Disposables )创建IDisposable

// Here we create a delegate from a lambda
Action action = () => service.SendMessageAsync(MessageType.Deploy);

// Here we subscribe
button.Clicked += action;

// We unsubscribe
var subscription = Disposable.Create(() => button.Clicked -= action);

// where it is necessary
subscription.Dispose();

Admit, this looks a bit lengthy and we lose the whole purpose of using lambda expressions. It is much safer and simpler to use common private methods to capture variables implicitly.

承认,这看起来有点冗长,我们失去了使用lambda表达式的全部目的。 使用通用私有方法隐式捕获变量更加安全和简单。

线程终止保护 (ThreadAbort protection)

When you create a library for an third-party developer, you cannot predict its behavior in a third-party application. Sometimes you can only guess what a programmer did to your library that caused a particular outcome. One example is functioning in a multithreaded environment when the consistency of resources cleanup can become a critical issue. Note that when we write the Dispose() method, we can guarantee the absence of exceptions. However, we cannot ensure that while running the Dispose() method no ThreadAbortException will occur that disables our thread of execution. Here we should remember that when ThreadAbortException occurs, all catch/finally blocks are executed anyway (at the end of a catch/finally block ThreadAbort occurs further along). So, to ensure execution of a certain code by using Thread.Abort you need to wrap a critical section in try { ... } finally { ... }, see the example below:

当您为第三方开发人员创建库时,无法预测其在第三方应用程序中的行为。 有时您只能猜测程序员对您的库所做的操作,从而导致特定的结果。 一个示例是在多线程环境中运行,此时资源清理的一致性可能成为一个关键问题。 请注意,当我们编写Dispose()方法时,可以保证没有异常。 但是,我们不能确保在运行Dispose()方法时不会发生ThreadAbortException从而禁用我们的执行线程。 在这里,我们应该记住,当ThreadAbortException发生时,无论如何都会执行所有catch / finally块(在catch / finally块的末尾,ThreadAbort会继续发生)。 因此,为了确保使用Thread.Abort执行某些代码,您需要在try { ... } finally { ... }包装一个关键部分,请参见以下示例:

void Dispose()
{
    if(_disposed) return;

    _someInstance.Unsubscribe(this);
    _disposed = true;
}

One can abort this at any point using Thread.Abort. It partially destroys an object, though you can still work with it in the future. At the same time, the following code:

可以随时使用Thread.Abort中止此操作。 尽管您将来仍可以使用它,但它会部分破坏对象。 同时,以下代码:

void Dispose()
{
    if(_disposed) return;

    // ThreadAbortException protection
    try {}
    finally
    {
        _someInstance.Unsubscribe(this);
        _disposed = true;
    }
}

is protected from such an abort and will run smoothly and for sure, even if Thread.Abort appears between calling Unsubscribe method and executing its instructions.

可以防止此类异常中止,并且即使在调用Unsubscribe方法与执行其指令之间出现Thread.Abort ,也可以确保平稳运行。

结果 (Results)

优点 (Advantages)

Well, we learned a lot about this simplest pattern. Let’s determine its advantages:

好吧,我们从最简单的模式中学到了很多。 让我们确定它的优点:

  1. The main advantage of the pattern is the capability to release resources determinately i.e. when you need them.

    该模式的主要优点是可以确定地释放资源,即在需要时释放资源。
  2. The second advantage is the introduction of a proven way to check if a specific instance requires to destroy its instances after using.

    第二个优点是引入了一种行之有效的方法来检查特定实例是否需要在使用后销毁其实例。
  3. If you implement the pattern correctly, a designed type will function safely in terms of use by third-party components as well as in terms of unloading and destroying resources when a process crashes (for example because of lack of memory). This is the last advantage.

    如果正确实现该模式,则设计的类型将在第三方组件使用以及进程崩溃(例如,由于内存不足)而卸载和销毁资源方面安全地起作用。 这是最后一个优势。

缺点 (Disadvantages)

In my opinion, this pattern has more disadvantages than advantages.

我认为,这种模式弊大于利。

  1. On the one hand, any type that implements this pattern instructs other parts that if they use it they take a sort of public offer. This is so implicit that as in case of public offers a user of a type doesn’t always know that the type has this interface. Thus you have to follow IDE prompts (type a period, Dis… and check if there is a method in the filtered member list of a class). If you see a Dispose pattern, you should implement it in your code. Sometimes it doesn’t happen straight away and in this case you should implement a pattern through a system of types that adds functionality. A good example is that IEnumerator<T> entails IDisposable.

    一方面,任何实现此模式的类型都会指示其他部分,如果他们使用它,则会进行某种公开报价。 这是如此隐含,以至于在公开发售的情况下,类型的用户并不总是知道该类型具有此接口。 因此,您必须遵循IDE提示(键入句号,Dis…,并检查类的筛选成员列表中是否存在方法)。 如果看到Dispose模式,则应在代码中实现它。 有时并不会立即发生,在这种情况下,您应该通过添加功能的类型系统来实现模式。 一个很好的例子是IEnumerator<T>需要IDisposable

  2. Usually when you design an interface there is a need to insert IDisposable into the system of a type’s interfaces when one of the interfaces have to inherit IDisposable. In my opinion, this damages the interfaces we designed. I mean when you design an interface you create an interaction protocol first. This is a set of actions you can perform with something hidden behind the interface. Dispose() is a method for destroying an instance of a class. This contradicts the essence of an interaction protocol. In fact, these are the details of implementation that infiltrated into the interface.

    通常,在设计接口时,当其中一个接口必须继承IDisposable时,需要将IDisposable插入类型接口的系统中。 我认为,这会损害我们设计的接口。 我的意思是在设计接口时,首先要创建一个交互协议。 这是一组可以用的东西隐藏在接口后面执行的操作。 Dispose()是用于销毁类实例的方法。 这与交互协议的本质相矛盾。 实际上,这些是渗透到接口中的实现细节。

  3. Despite being determined, Dispose() doesn’t mean direct destruction of an object. The object will still exist after its destruction but in another state. To make it true CheckDisposed() must be the first command of each public method. This looks like a temporary solution that somebody gave us saying: “Go forth and multiply”;

    尽管已确定,但Dispose()并不意味着直接销毁对象。 该对象在销毁后仍将存在,但处于另一种状态。 要使其正确,CheckDisposed()必须是每个公共方法的第一个命令。 这似乎是有人给我们说的临时解决方案:

  4. There is also a small chance to get a type that implements IDisposable through explicit implementation. Or you can get a type that implements IDisposable without a chance to determine who must destroy it: you or the party that gave it to you. This resulted in an antipattern of multiple calls of Dispose() that allows to destroy a destroyed object;

    通过显式实现获得实现IDisposable的类型的机会也很小。 或者,您可以获得实现IDisposable的类型,而没有机会确定谁必须销毁它:您或提供它的一方。 这导致了对Dispose()多次调用的反模式,该模式允许销毁已销毁的对象。

  5. The complete implementation is difficult, and it is different for managed and unmanaged resources. Here the attempt to facilitate the work of developers through GC looks awkward. You can override virtual void Dispose() method and introduce some DisposableObject type that implements the whole pattern, but that doesn’t solve other problems connected with the pattern;

    完整的实现很困难,对于托管资源和非托管资源而言,它是不同的。 在这里,通过GC促进开发人员的工作的尝试看起来很尴尬。 您可以重写virtual void Dispose()方法,并引入一些实现整个模式的DisposableObject类型,但不能解决与该模式有关的其他问题。

  6. As a rule Dispose() method is implemented at the end of a file while '.ctor' is declared at the beginning. If you modify a class or introduce new resources, it is easy to forget to add disposal for them.

    通常,Dispose()方法在文件末尾实现,而'.ctor'在开头声明。 如果您修改类或引入新资源,则很容易忘记为它们添加处理方法。
  7. Finally, it is difficult to determine the order of destruction in a multithreaded environment when you use a pattern for object graphs where objects fully or partially implement that pattern. I mean situations when Dispose() can start at different ends of a graph. Here it is better to use other patterns, e.g. the Lifetime pattern.

    最后,当将模式用于对象图时,很难确定破坏在多线程环境中的顺序,其中对象完全或部分实现该模式。 我的意思是Dispose()可以从图形的不同末端开始的情况。 在这里最好使用其他模式,例如生命周期模式。

  8. The wish of platform developers to automate memory control combined with realities: applications interact with unmanaged code very often + you need to control the release of references to objects so Garbage Collector could collect them. This adds great confusion in understanding such questions as: “How should we implement a pattern correctly”? “Is there a reliable pattern at all”? Maybe calling delete obj; delete[] arr; is simpler?

    平台开发人员希望结合实际情况自动执行内存控制:应用程序经常与非托管代码进行交互+您需要控制对对象的引用的释放,以便Garbage Collector可以收集它们。 这在理解以下问题时增加了极大的困惑:“我们应该如何正确实施模式”? “是否有可靠的模式”? 也许调用delete obj; delete[] arr; delete obj; delete[] arr; 更简单?

域卸载并退出应用程序 (Domain unloading and exit from an application)

If you got to this part, you became more confident in the success of future job interviews. However, we didn’t discuss all the questions connected with this simple, as it may seem, pattern. The last question is whether the behavior of an application differs in case of simple garbage collection and when garbage is collected during domain unloading and while exiting the application. This question merely touches upon Dispose()… However Dispose() and finalization go hand in hand and we rarely meet an implementation of a class which has finalization but doesn't have Dispose() method. So, let’s describe finalization in a separate section. Here we just add a few important details.

如果您掌握了这一部分,您将对以后的工作面试的成功充满信心。 但是,我们并未讨论与这个简单的(似乎是)模式有关的所有问题。 最后一个问题是,在简单垃圾收集的情况下以及在域卸载期间和退出应用程序时收集垃圾时,应用程序的行为是否有所不同。 这个问题仅涉及Dispose() …但是Dispose()和finalization并存,而我们很少遇到具有finalize但没有Dispose()方法的类的实现。 因此,让我们在单独的部分中描述终结处理。 在这里,我们仅添加一些重要的细节。

During application domain unloading you unload both assemblies loaded into the application domain and all objects that were created as part of the domain to be unloaded. In fact, this means the cleanup (collection by GC) of these objects and calling finalizers for them. If the logic of a finalizer waits for finalization of other objects to be destroyed in the right order, you may pay attention to Environment.HasShutdownStarted property indicating that an application is unloaded from memory and to AppDomain.CurrentDomain.IsFinalizingForUnload() method indicating that this domain is unloaded which is the reason for finalization. If these events occur the order of resources finalization generally becomes unimportant. We cannot delay either the unloading of domain or an application as we should do everything as quickly as possible.

在应用程序域卸载期间,您将卸载加载到应用程序域中的两个程序集以及作为要卸载域的一部分而创建的所有对象。 实际上,这意味着清除(通过GC收集)这些对象并为它们调用终结器。 如果终结器的逻辑等待其他对象的终结以正确的顺序进行终结,则您可能要注意Environment.HasShutdownStarted属性(指示从内存中卸载应用程序)和AppDomain.CurrentDomain.IsFinalizingForUnload()方法,以指示此操作已从内存中卸载。域已卸载,这是完成的原因。 如果发生这些事件,则资源确定的顺序通常变得不重要。 我们不能延迟域或应用程序的卸载,因为我们应该尽快完成所有工作。

This is the way this task is solved as part of a class LoaderAllocatorScout

这是作为类LoaderAllocatorScout的一部分解决此任务的方式

// Assemblies and LoaderAllocators will be cleaned up during AppDomain shutdown in
// an unmanaged code
// So it is ok to skip reregistration and cleanup for finalization during appdomain shutdown.
// We also avoid early finalization of LoaderAllocatorScout due to AD unload when the object was inside DelayedFinalizationList.
if (!Environment.HasShutdownStarted &&
    !AppDomain.CurrentDomain.IsFinalizingForUnload())
{
    // Destroy returns false if the managed LoaderAllocator is still alive.
    if (!Destroy(m_nativeLoaderAllocator))
    {
        // Somebody might have been holding a reference on us via weak handle.
        // We will keep trying. It will be hopefully released eventually.
        GC.ReRegisterForFinalize(this);
    }
}

典型的实施故障 (Typical implementation faults)

As I showed you there is no universal pattern to implement IDisposable. Moreover, some reliance on automatic memory control misleads people and they make confusing decisions when implementing a pattern. The whole .NET Framework is riddled with errors in its implementation. To prove my point, let’s look at these errors using the example of .NET Framework exactly. All implementations are available via: IDisposable Usages

正如我向您展示的,没有实现IDisposable的通用模式。 而且,对自动内存控制的某些依赖会误导人们,并且在实施模式时他们会做出令人困惑的决策。 整个.NET Framework实施过程中充满错误。 为了证明我的观点,让我们确切地使用.NET Framework示例查看这些错误。 所有实现均可通过以下方式获得: IDisposable用法

FileEntry Class cmsinterop.cs

FileEntry类 cmsinterop.cs

This code is written in a hurry just to close the issue. Obviously, the author wanted to do something but changed his mind and kept a flawed solution
匆忙编写此代码只是为了解决问题。 显然,作者想做点什么,但改变了主意,并提出了有缺陷的解决方案
internal class FileEntry : IDisposable
{
    // Other fields
    // ...
    [MarshalAs(UnmanagedType.SysInt)] public IntPtr HashValue;
    // ...

    ~FileEntry()
    {
        Dispose(false);
    }

    // The implementation is hidden and complicates calling the *right* version of a method.
    void IDisposable.Dispose() { this.Dispose(true); }

    // Choosing a public method is a serious mistake that allows for incorrect destruction of
    // an instance of a class. Moreover, you CANNOT call this method from the outside
    public void Dispose(bool fDisposing)
    {
        if (HashValue != IntPtr.Zero)
        {
            Marshal.FreeCoTaskMem(HashValue);
            HashValue = IntPtr.Zero;
        }

        if (fDisposing)
        {
            if( MuiMapping != null)
            {
                MuiMapping.Dispose(true);
                MuiMapping = null;
            }

            System.GC.SuppressFinalize(this);
        }
    }
}

SemaphoreSlim Class System/Threading/SemaphoreSlim.cs

SemaphoreSlim类 系统/线程/SemaphoreSlim.cs

This error is in the top of errors of .NET Framework regarding IDisposable: SuppressFinalize for classes where there is no finalizer. It is very common.
此错误是有关IDisposable的.NET Framework错误的顶部,即:没有终结器的类的SuppressFinalize。 这是很常见的。
public void Dispose()
{
    Dispose(true);

    // As the class doesn’t have a finalizer, there is no need in GC.SuppressFinalize
    GC.SuppressFinalize(this);
}

// The implementation of this pattern assumes the finalizer exists. But it doesn’t.
// It was possible to do with just public virtual void Dispose()
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        if (m_waitHandle != null)
        {
            m_waitHandle.Close();
            m_waitHandle = null;
        }
        m_lockObj = null;
        m_asyncHead = null;
        m_asyncTail = null;
    }
}

Calling Close+Dispose Some NativeWatcher project code

调用Close + Dispose 一些NativeWatcher项目代码

Sometimes people call both Close and Dispose. This is wrong though it will not produce an error as the second Dispose doesn’t generate an exception.
有时人们将关闭和处置同时称为。 这是错误的,尽管它不会产生错误,因为第二个Dispose不会生成异常。

In fact, Close is another pattern to make things clearer for people. However, it made everything more unclear.

实际上,“关闭”是另一种使人更清楚的方式。 但是,这使一切变得更加不清楚。

public void Dispose()
{
    if (MainForm != null)
    {
        MainForm.Close();
        MainForm.Dispose();
    }
    MainForm = null;
}

一般结果 (General results)

  1. IDisposable is a standard of the platform and the quality of its implementation influences the quality of the whole application. Moreover, in some situation it influences the safety of your application that can be attacked via unmanaged resources.

    IDisposable是平台的标准,其实现的质量会影响整个应用程序的质量。 此外,在某些情况下,它会影响您的应用程序的安全性,并可能受到不受管理的资源的攻击。
  2. The implementation of IDisposable must be maximally productive. This is especially true about the section of finalization, that works in parallel with the rest of code, loading Garbage Collector.

    IDisposable的实现必须具有最大的生产力。 对于完成部分,这与其余代码并行工作(加载Garbage Collector)特别重要。
  3. When implementing IDisposable you shouldn't use Dispose() simultaneously with public methods of a class. The destruction cannot go along with usage. This should be considered when designing a type that will use IDisposable object.

    在实现IDisposable时,不应与类的公共方法同时使用Dispose()。 销毁不能伴随使用。 在设计将使用IDisposable对象的类型时应考虑这一点。
  4. However, there should be a protection against calling ‘Dispose()’ from two threads simultaneously. This results from the statement that Dispose() shouldn’t produce errors.

    但是,应该有防止同时从两个线程调用'Dispose()'的保护措施。 这是由于Dispose()不应产生错误的陈述导致的。
  5. Types that contain unmanaged resources should be separated from other types. I mean if you wrap an unmanaged resource, you should allocate a separate type for it. This type should contain finalization and should be inherited from SafeHandle / CriticalHandle / CriticalFinalizerObject. This separation of responsibility will result in improved support of the type system and will simplify the implementation to destroy instances of types via Dispose(): the types with this implementation won't need to implement a finalizer.

    包含非托管资源的类型应与其他类型分开。 我的意思是,如果包装了非托管资源,则应为其分配一个单独的类型。 此类型应包含终结处理,并且应继承自SafeHandle / CriticalHandle / CriticalFinalizerObject 。 责任的这种分离将导致改进对类型系统的支持,并将简化通过Dispose()销毁类型实例的实现:具有此实现的类型将不需要实现终结器。

  6. In general, this pattern is not comfortable in use as well as in code maintenance. Probably, we should use Inversion of Control approach when we destroy the state of objects via Lifetime pattern. However, we will talk about it in the next section.

    通常,此模式在使用和代码维护中都不方便。 当通过Lifetime模式销毁对象的状态时,可能应该使用控制反转方法。 但是,我们将在下一节中讨论它。

This chapter was translated from Russian jointly by author and by
professional translators. You can help us with translation from Russian or English into any other language, primarily into Chinese or German.
Also, if you want thank us, the best way you can do that is to give us a star on github or to fork repository .
本章由作者和
专业翻译员共同译自俄语。 您可以帮助我们将俄语或英语翻译成任何其他语言,主要是中文或德语。
另外,如果您想感谢我们,最好的方法是在github上给我们加星号或分支存储库 github / sidristij / dotnetbook

翻译自: https://habr.com/en/company/clrium/blog/443962/

pt622光猫切换模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值