3

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)?

9
  • 7
    No, only context. Commented Aug 17, 2022 at 9:54
  • Not sure what you mean by "destroy"; it ensures the Dispose method is called when context is out of scope. Any variables declared within the block are unaffected. Commented Aug 17, 2022 at 9:57
  • 1
    If it was disposing everything inside using, how could you use anything there that should not be disposed? Then using was unusable. Also, only the instance used in the using is disposed, not everything that this instance is using. The dispose method of this object has to take care of that. Commented Aug 17, 2022 at 10:00
  • 1
    Just add using: using SQLEmployeeData sqlData = new SQLEmployeeData(context); and sqlData will be disposed on leaving its scope Commented Aug 17, 2022 at 10:04
  • 3
    If it had worked as you supposed, why do you think the language designers would have forced you to pick out one of the variables to put inside the () part? Why would they not have given us using { ... } as the complete block? Commented Aug 17, 2022 at 10:07

3 Answers 3

8

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.
Sign up to request clarification or add additional context in comments.

3 Comments

Strictly, in release mode a variable no longer keeps an object alive if it is provably never dereferenced again. The GC doesn't have to wait until the end of the block. It's possible to create contrived circumstances where e.g. an object can be collected whilst its constructor is still running (provided any variable it is going to be assigned to, if any, is never read and provided the remainder of the constructor no longer explicitly or implicitly makes use of the this reference)
@Damien_The_Unbeliever I opted for a simpler upper boundary to explain the basics, but you are correct that this can be micromanaged further.
It is INCORRECT to say that "if the object in question is IDisposable, the garbage collector will be smart enough to call its Dispose method." The GC calls the Finalizer if it exists. Thus, if you forget to call Dispose on an object without a Fianlizer, its Dispose method is never called.
2

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.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.