Is it possible to force the use of “using” for dis

2019-01-13 20:29发布

问题:

I need to force the use of "using" to dispose a new instance of a class.

public class MyClass : IDisposable
{
   ...
}

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

回答1:

The fact that you need to ensure that the object is disposed indicates a design flaw. It's fine if disposing is the polite or efficient thing to do, but it should not be semantically necessary.

There is no way to enforce that an object is disposed of via the using statement. However, what you can do is maintain a flag in the object that indicates whether the object was disposed or not, and then write a finalizer that checks that flag. If the finalizer detects that the object wasn't disposed, then you can have the finalizer, say, terminate the process via failfast. That is, so severely punish the user who neglected to dispose the object that they are forced to either fix their bug or stop using your object.

That doesn't strike me as nice, good, or polite, but you're the only one who knows what the terrible, terrible consequences are of failing to dispose the object. Whether applying a punishment to people who fail to follow your crazy rules is better than living with the consequences of them failing to follow the rules is for you to decide.



回答2:

It's ugly, but you could do something like this:

    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);
            }
        }
    }


回答3:

You can write your own warning/error with the use of the Roslyn framework. Your DiagnosticAnalyzer would check all constructor calls to see if your class is being constructed or not and if you are within a using statement or not.

The reported diagnostic can be set to Error severity, and can be marked as non-configurable, meaning that nobody can downgrade it to warning or info.

Also, if you are developing a Nuget library you might want to ship your analyzer as a development dependency and add it as an analyzer nuget package. This would result in all of your users being forced to dispose your given class. This packaging is referred to as "code-aware library".

Note that in theory this could be done by a third party analyzer library too (such as FxCop), but there are many IDisposable implementations that do not strictly need to be disposed, such as MemoryStream, whose Dispose doesn't do a whole lot, so these rules either have some white-listing mechanisms or report false positives.



回答4:

The using statement is a shorthand that the compiler converts from:

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

into:

DisposableObject d = new DisposableObject()
try
{

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

so you are more or less asking if it is possible to enforce writing a try/finally block that calls Dispose for an object.



回答5:

I wonder if FXCop could enforce that rule?



回答6:

No, you cannot do that. You can't even force them to call dispose. The best you can do is add a finalizer. Just keep in mind that the finalizer will get called when the object is disposed and that is up to the runtime.



回答7:

No it is not possible. Now what you can do is call the dispose method in the finalizer of the class (and then you can suppress the use of it if they do actually call the dispose method). That way it will fire if not done explicitly in code.

This link will show you how to implement the finalizer / dispose pattern:

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



回答8:

If you want force to use using on this class, your code to support this class you might code in other class and hide MyClass for normal use.



回答9:

You should look into RAII, which is a technique that ensures that the acquired resources will be properly disposed.

What I mean is that if you can't force the Dispose method to be called (through using or directly), you can put its content inside another method that will be called, such as the destructor.

It is a common pattern to implement IDisposable as below:

// 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);
}

Source: http://msdn.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx



回答10:

If you want to force the disposal of resources withing a scope it's possible, but IDisposable is not really needed. With the following code:

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
}

You can use the Resource ONLY in this way:

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

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

As you can see IDisposable is not really needed here.