Why should I encapsulate objects in using if there

2019-08-26 08:42发布

问题:

This question already has an answer here:

  • What is the C# Using block and why should I use it? [duplicate] 9 answers

I was searching this on the internet and with no success. So hopefully someone here can answer me this because I don't understand the point of it.

  • Is it just good practice or it actually does something?
  • Why should I bother if .NET has very good garbage collector?

Example

In this example I will use super-simple data-annotation validation filter. What is the benefit of encapsulating my data context in using?

public static ValidationResult ValidateUniqueUsername(string username, object context)
{
    using (var db = new MainDataContext()) // What's the point?
    {
        var user = db.Users.SingleOrDefault(x => x.Username == username);

        if (user == null) return ValidationResult.Success;
        return new ValidationResult("Username already taken");
    }
}

回答1:

The using clause basically unwraps to this:

ISomeDisposable disposable = new SomeDisposable();
try
{
    // your code here
}
finally
{
    if(disposable != null)
        disposable.Dispose();
}

So, as you can see, if your code throws an exception, the disposable object is Disposed. If you don't thrown an exception, it is still disposed (hence the use of the finally block). It ensures your object is disposed.



回答2:

A using block is just syntactic sugar for a Try...Finally block. So your code is the equivalent of this

MainDataContext db = new MainDataContext();
try
{
   var user = db.Users.SingleOrDefault(x => x.Username == username);

   if (user == null) return ValidationResult.Success;
       return new ValidationResult("Username already taken");
}
finally
{
    if (db != null)
      ((IDisposable)db).Dispose();
}

This means that regardless of the return call, db's Dispose method is called (because it's in a finally block). Generally speaking it's always a good idea to dispose of any object that implements IDisposable whether it's managed or not; although there are a few exceptions.

In a managed scenario this may or may not be too important, although I recommend sticking with best-practice, but Dispose is primarily used for unmanaged resources. These resources can remain thus causing a memory leak which is why it's a good idea to dispose of them when you've finished using them. From MSDN:

The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams.



回答3:

Some objects are Disposable. That means they use external resources (Files pointer, sockets) that arn't in the GC scope (lower level resources).

These resources need to be disposed (freed) before the object that hold them is destroyed. They can cause a memory leak if the object is removed by the GC whithout calling the dispose method before.

The using block take care of calling the dispose automaticly at the end of the block. It is similar to:

Obj obj;
try{
      obj = new Obj();
      //code
}
catch(Exception e){
      //error
}
finally{
      obj.Dispose();
}

The Obj class must implements the Disposable interface to be disposable. An using block can't be used with a non-disposable object (at least in C#).

It is generally a good pratice to use disposable object inside an using block.



回答4:

Encapsulating the data in "using" makes sure that the object is disposed off as soon as it is out of the "using" scope.

Garbage collector, on the other hand, disposes an object, when it determines that the object is out of scope or when no longer needed.

In MSDN's way: Using Provides a convenient syntax that ensures the correct use of IDisposable objects.

As the other answer says, @(Moo-Juice)

"it ensures your object is disposed."



回答5:

First - read about IDisposable pattern and its purpose. Note the first sentence:

The primary use of this interface is to release unmanaged resources.

That's the point. Garbage Collection is about realising the memory not resources. Here is where IDisposable comes with help. Read the chapter Manipulating Unmanaged Resources of Fundamentals of Garbage Collection. Notice, that:

If your managed objects reference unmanaged objects by using their native file handles, you have to explicitly free the unmanaged objects, because the garbage collector tracks memory only on the managed heap.

Garbage Collection is an undeterministic mechanism. You can declare a finalizer in your class but you can't be sure when it will be called. Therefore your unmanaged resources can be hold without any reason.