Home > database >  Does using statement dispose the objects instantiated inside the block in C#?
Does using statement dispose the objects instantiated inside the block in C#?

Time:08-17

Here is the code:

using(var context = new FirstAppDemoDbContext())
{
     SQLEmployeeData sqlData = new SQLEmployeeData(context);
     model.Employees = sqlData.GetAll();
}

I know that the using statement will destroy the "context" object after finishing with using block, but I wonder if it will destroy the object instantiated inside the using block (I mean "sqlData"). Does it destroy the "sqlData" object as well?

More generally, Does the using block destroy whatever is defined inside the block (inside bracket)?

CodePudding user response:

No, but...

Any variable that was declared inside the block, i.e. between the {}, will go out of scope once you exit the block (which happens at the same time as the using will dispose of the resource), and when it goes out of scope, the garbage collector might activate to collect the object it used to reference.

Garbage collection

In short, the garbage collector is a system that automatically comes and collects any object that is no longer being referenced. In other words, if everyone has forgotten about it, it can be removed. If the object in question is IDisposable, the garbage collector will be smart enough to call its Dispose method.

This is in sharp contrast to the using method, which will dispose your object even if others are still referencing it.

A second thing to point out is that the garbage collector comes when he wants to. There are ways to force him to come; but generally speaking you won't be managing this, and the .NET runtime will send the garbage collector when it wants to do so.

Let's use a short example here. I'm using an if here, but the same would be true of any scope block (commonly bounded by {})

Person a;

if(true)
{
    Person b = new Person("Bob");
    Person c = new Person("Cindy");
    a = c;
}

// We have now exited the block

When you reach the comment, consider the object that b refers to (which I will call Bob from this point on).

This object is referenced only by the variable b. When we exited the if block, b went out of scope and no longer exists. Therefore, we can say for certain that no one is still referencing Bob. Therefore, when the garbage collector comes, he will collect Bob and dispose of him.

However, since the garbage collector comes whenever he want to; we cannot be certain that he has already come by to collect Bob. Bob may still be in memory, or he may already have been disposed on. In general, we don't care. The runtime will manage that part for us.

Let's consider the object referenced by a and c (which I will call Cindy from this point on).

This object is referenced by two variables: a and c. When we exited the if block, c went out of scope and no longer exists. However, a is still in scope.
Therefore, we can conclude that someone is still referencing Cindy. Therefore, when the garbage collector comes, he will not collect Cindy and dispose of her.

Back to your example

When you hit the end of the using block, several things happen:

  • The runtime explicitly disposes of the object referenced by context
    • Note that the runtime does not care whether there are other variables which have not yet gone out of scope that are referencing this object. It just doesn't care. It was told to dispose, therefore it disposes.
  • Because we exit the block, the sqlData variable goes out of scope.
    • This object is referenced only by the variable sqlData. Therefore, we can say for certain that no one is still referencing this object. Therefore, when the garbage collector comes, he will collect the object and dispose of it.
    • However, we don't know if the garbage collector has already come for this object. Maybe it's still in memory somewhere, maybe it's already been collected.
  • model did not go out of scope as it was declared on a higher level, so the garbage collector will not attempt to collect the object referenced by model.
  • The object returned by sqlData.GetAll() is still being referenced by model.Employees, and therefore the garbage collector will not attempt to collect it.

In short

  • Using statements immediately dispose of the explicitly declared resource (between the using( and ))
  • When you exit a block ({}), any objects that were only being referenced by variables that now went out of scope, are going to be collected by the garbage collector, but not necessarily immediately.

CodePudding user response:

The using statement does not "destroy" anything that is defined/instantiated within it's block.

Using statements are used to called Dispose on disposable objects when the go out of scope - this is important as Dispose is primarily used for freeing up unmanaged resources.

Whatever is defined/instantiated within a using block will eventually go out of scope and be collected by the garbage collector - when the objects go out of scope will depend on if they were defined within the block or before it / where they are last referenced.

CodePudding user response:

By looking at the "lowered" code of the compiler output, you can see more clearly exactly what the using statement is doing.

Model model = new Model ();
FirstAppDemoDbContext context = new FirstAppDemoDbContext();
try
{
    SQLEmployeeData sqlData = new SQLEmployeeData(context);
    model.Employees = sqlData.GetAll();
}
finally
{
    if (context != null)
    {
         ((IDisposable)context).Dispose();
    }
}

The using statement simply compiles to a try/finally block that calls dispose on context in the finally block as long as context is not null.

Nothing interacts with sqlData or model inside of the try block. If you need to handle those resources (outside of normal garbage collection), you would need to do so inside the using statement itself.

  •  Tags:  
  • c#
  • Related