Можно ли принудительно использовать «использование» для одноразовых занятий? - PullRequest
32 голосов
/ 20 апреля 2010

Мне нужно принудительно использовать «using» для удаления нового экземпляра класса.

public class MyClass : IDisposable
{
   ...
}

using(MyClass obj = new MyClass()) // Force to use "using"
{
}

Ответы [ 10 ]

45 голосов
/ 20 апреля 2010

Тот факт, что вам нужно , чтобы убедиться, что объект расположен, указывает на недостаток дизайна. Это нормально, если утилизация является вежливым или эффективным делом, но это не должно быть семантически необходимым .

Невозможно добиться принудительного удаления объекта с помощью оператора using. Однако, что вы можете сделать, это сохранить флаг в объекте, который указывает, был ли объект удален или нет, а затем написать финализатор, который проверяет этот флаг. Если финализатор обнаружит, что объект не был удален, то вы можете иметь финализатор, скажем, завершить процесс через failfast. То есть настолько сурово наказывают пользователя, который пренебрег распоряжением объектом, что он вынужден либо исправить свою ошибку, либо прекратить использование вашего объекта.

Это не кажется мне хорошим, хорошим или вежливым, но вы единственный, кто знает, каковы ужасные, ужасные последствия неспособности избавиться от объекта. Лучше ли применять наказание к людям, которые не следуют вашим сумасшедшим правилам, чем жить с последствиями их несоблюдения правил, решать вам.

21 голосов
/ 20 апреля 2010

Это ужасно, но вы могли бы сделать что-то вроде этого:

    public sealed class DisposableClass : IDisposable
    {
        private DisposableClass()
        {

        }

        public void Dispose()
        {
            //Dispose...
        }

        public static void DoSomething(Action<DisposableClass> doSomething)
        {
            using (var disposable = new DisposableClass())
            {
                doSomething(disposable);
            }
        }
    }
13 голосов
/ 23 мая 2016

Вы можете написать собственное предупреждение / ошибку с использованием фреймворка Roslyn. Ваш DiagnosticAnalyzer проверит все вызовы конструктора, чтобы увидеть, создается ли ваш класс или нет, и если вы в пределах оператора using или нет.

Указанная диагностика может быть установлена ​​на серьезность ошибки и может быть помечена как неконфигурируемая, что означает, что никто не может понизить ее до предупреждения или информации.

Кроме того, если вы разрабатываете библиотеку Nuget, возможно, вы захотите отправить свой анализатор как зависимость для разработки и добавить его в виде пакета nuget анализатора. Это приведет к тому, что все ваши пользователи будут вынуждены избавиться от вашего класса. Эта упаковка называется "библиотека с поддержкой кода" .

Обратите внимание, что теоретически это может быть сделано сторонней библиотекой анализатора (такой как FxCop), но есть много реализаций IDisposable, которые не обязательно должны быть утилизированы, таких как MemoryStream, чья Dispose не очень много, поэтому в этих правилах либо есть механизмы белого списка, либо они сообщают о ложных срабатываниях.

5 голосов
/ 20 апреля 2010

Оператор using - это сокращение, из которого компилятор преобразует:

(using DisposableObject d = new DisposableObject()){}

в

DisposableObject d = new DisposableObject()
try
{

}
finally
{
    if(d != null) d.Dispose();
}

так что вы более или менее спрашиваете, возможно ли принудительно написать блок try / finally, который вызывает Dispose для объекта.

4 голосов
/ 20 апреля 2010

Интересно, может ли FXCop применить это правило?

1 голос
/ 20 апреля 2010

Нет, это невозможно. Теперь вы можете вызвать метод dispose в финализаторе класса (и затем вы можете запретить его использование, если они действительно вызывают метод dispose). Таким образом, он сработает, если это явно не сделано в коде.

Эта ссылка покажет вам, как реализовать шаблон финализатора / утилизации:

http://www.devx.com/dotnet/Article/33167

1 голос
/ 20 апреля 2010

Нет, вы не можете этого сделать. Вы даже не можете заставить их позвонить распоряжаться. Лучшее, что вы можете сделать, это добавить финализатор. Просто помните, что финализатор будет вызван при удалении объекта, и это до времени выполнения.

0 голосов
/ 10 июня 2011

Если вы хотите принудительно утилизировать ресурсы внутри области, это возможно, но IDisposable на самом деле не нужен. Со следующим кодом:

public class ResourceHandle
{
    public delegate void ResourceProvider(Resource resource);

    private string _parms;

    public ResourceHandle(string parms)
    {
        _parms = parms;
    }

    public void UseResource(ResourceProvider useResource)
    {
        Resource resource = new Resource(_parms);
        useResource(resource);
        resource.Close();
    }
}


public class Resource
{
    private string _parms;

    internal Resource(string parms)
    {
        // Initialize resource
    }

    internal void Close()
    {
        // Do cleaning
    }

    // Public methods of resource
}

Вы можете использовать ресурс ТОЛЬКО таким образом:

public void foo()
{
    ResourceHandle resourceHandle = new ResourceHandle("parms");

    resourceHandle.UseResource(delegate(Resource resource)
        {
            // use resource
        });
}

Как видите, IDisposable здесь на самом деле не нужен.

0 голосов
/ 20 апреля 2010

Вы должны изучить RAII , который является техникой, которая гарантирует, что полученные ресурсы будут правильно распределены.

Я имею в виду, что если вы не можете принудительно вызвать метод Dispose (через using или напрямую), вы можете поместить его содержимое в другой метод, который будет вызываться, например, в деструктор.

Это общая схема реализации IDisposable, как показано ниже:

// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
    Dispose(true);
    // This object will be cleaned up by the Dispose method.
    // Therefore, you should call GC.SupressFinalize to
    // take this object off the finalization queue 
    // and prevent finalization code for this object
    // from executing a second time.
    GC.SuppressFinalize(this);
}

// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the 
// runtime from inside the finalizer and you should not reference 
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
    // Check to see if Dispose has already been called.
    if(!this.disposed)
    {
        // If disposing equals true, dispose all managed 
        // and unmanaged resources.
        if(disposing)
        {
            // Dispose managed resources.
            component.Dispose();
        }

        // Call the appropriate methods to clean up 
        // unmanaged resources here.
        // If disposing is false, 
        // only the following code is executed.

        // TODO: write code
    }
    disposed = true;         
}

// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method 
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~ClassName()
{
    // Do not re-create Dispose clean-up code here.
    // Calling Dispose(false) is optimal in terms of
    // readability and maintainability.
    Dispose(false);
}

Источник: http://msdn.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx

0 голосов
/ 20 апреля 2010

Если вы хотите принудительно использовать использование в этом классе, ваш код для поддержки этого класса вы можете кодировать в другом классе и скрывать MyClass для обычного использования.

...