Using IDisposable Part 1: How to use IDisposable objects

AID: 3418
  • Status: Published

2573 points

  • Byzadeveloper
  • TypeGeneral
  • Posted on2010-07-15 at 09:36:51
Awards
  • Community Pick
Introduction

This article series is supposed to shed some light on the use of IDisposable and objects that inherit from it. In essence, a more apt title for this article would be: using (IDisposable) {}. I’m just not sure how many people would get that off the bat and you are one of them you should understand by the end of the Part 1.  Part 2 will deal with creating objects and inheriting from IDisposable.

With wonderful things like disposable objects and garbage collection I feel most of our developer colleagues have forgotten about memory management and memory leaks. If you are one of those people who would say: “There is so much memory available these days we don’t need to worry about memory”, then you fall into 1 of 2 categories.

  • You have not developed anything more complex than a “hello world” app
  • You are a VB developer

Either way you don’t need to continue reading. (This article is meant for developers) ?

Before getting into the article consider the following priorities when developing:
  • Code must execute as fast as possible (yes I mean shaving off 1/(1 000 000) of a millisecond)
  • The application must be as small as possible (no useless processor executions)


How to use an object of type IDisposable

Let’s assume that you want to open a connection to Sql Server. There are 2 ways to do this with executing the .Dispose() method. By the way – you have to call .Dispose() on a disposable object to free it from memory. Don’t leave this takes to the garbage collector, as you know they don’t make much cash and often don’t do their job properly. For example if I write a class and don’t call dispose from my destructor – when the variable is out of scope and the destructor is called IDispose might not be – depending how good a developer I am. Let’s look at these functions:
       
public void TestA()
        {
            using (var cn = new SqlConnection("Some String A"))
            {
                cn.Open();
            }
        }
                                    
1:
2:
3:
4:
5:
6:
7:

Select allOpen in new window


...and...
 public void TestB()
        {
            var cn = new SqlConnection("Some String B");
            cn.Open();
            cn.Dispose();
        }
                                    
1:
2:
3:
4:
5:
6:

Select allOpen in new window


Now, for some of you, this may come as a shock to see the using keyword used anywhere else but the top of your source code file. Actually what it is doing is declaring a block between {} where the object defined in the () can be used (and by the way the object has to inherit from IDisposable). Once the code leaves the block the .Dispose() method is called on the object weather it leaves gracefully or via an exception being thrown. The object then becomes out of scope.  Let’s have a look at the IL code that is created for these 2 functions.
.method public hidebysig instance void TestA() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection cn,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: ldstr "Some String A"
    L_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
    L_000b: stloc.0 
    L_000c: nop 
    L_000d: ldloc.0 
    L_000e: callvirt instance void [System.Data]System.Data.Common.DbConnection::Open()
    L_0013: nop 
    L_0014: nop 
    L_0015: leave.s L_0027
    L_0017: ldloc.0 
    L_0018: ldnull 
    L_0019: ceq 
    L_001b: stloc.1 
    L_001c: ldloc.1 
    L_001d: brtrue.s L_0026
    L_001f: ldloc.0 
    L_0020: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0025: nop 
    L_0026: endfinally 
    L_0027: nop 
    L_0028: ret 
    .try L_000c to L_0017 finally handler L_0017 to L_0027
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:

Select allOpen in new window



.method public hidebysig instance void TestB() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection cn)
    L_0000: nop 
    L_0001: ldstr "Some String B"
    L_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
    L_000b: stloc.0 
    L_000c: ldloc.0 
    L_000d: callvirt instance void [System.Data]System.Data.Common.DbConnection::Open()
    L_0012: nop 
    L_0013: ldloc.0 
    L_0014: callvirt instance void [System]System.ComponentModel.Component::Dispose()
    L_0019: nop 
    L_001a: ret 
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:

Select allOpen in new window



As you can see TestA creates more execution code than TestB and they do exactly the same thing, violating our “small as possible” rule. Exactly? Well no … not exactly. You see, if an exception is thrown at let’s say: Open(): TestB will not execute the .Dispose() method, while TestA will. So we need to modify TestB to become TestC to become more like TestA. It looks like this:
public void TestC()
        {
            SqlConnection cn = null;
            try
            {
                cn = new SqlConnection("Some String C");
                cn.Open();
            }
            finally 
            {
                cn.Dispose();
            }
        }
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:

Select allOpen in new window


.method public hidebysig instance void TestC() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection cn)
    L_0000: nop 
    L_0001: ldnull 
    L_0002: stloc.0 
    L_0003: nop 
    L_0004: ldstr "Some String C"
    L_0009: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
    L_000e: stloc.0 
    L_000f: ldloc.0 
    L_0010: callvirt instance void [System.Data]System.Data.Common.DbConnection::Open()
    L_0015: nop 
    L_0016: nop 
    L_0017: leave.s L_0023
    L_0019: nop 
    L_001a: ldloc.0 
    L_001b: callvirt instance void [System]System.ComponentModel.Component::Dispose()
    L_0020: nop 
    L_0021: nop 
    L_0022: endfinally 
    L_0023: nop 
    L_0024: ret 
    .try L_0003 to L_0019 finally handler L_0019 to L_0023
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:

Select allOpen in new window



Well, that looks very close to the IL code for TestA and it does the same thing. In fact TestA has this extra:
It declares and uses an extra boolean value. So comparing TestA and TestC, removing all the non-executing lines we have TestA producing 23 executable lines and TestC producing 21 executable lines.

So TestC wins when it comes to executing speed (by 2 executions) and TestA wins the “neat code” award.  Bearing in mind that you probably have a try… catch anyway, TestC is the best way to go.

Just for giggles, let’s see what happens if you put a try finally (I know you should have a try catch there anyway) around using in TestA (which is possibly the case anyway).
public void TestA2()
        {
            try
            {
                using (var cn = new SqlConnection("Some String A"))
                {
                    cn.Open();
                }
            }
            finally
            {
              //do stuff
            }            
        }
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:

Select allOpen in new window


...and...
.method public hidebysig instance void TestA2() cil managed
{
    .maxstack 2
    .locals init (
        [0] class [System.Data]System.Data.SqlClient.SqlConnection cn,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: nop 
    L_0002: ldstr "Some String A"
    L_0007: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
    L_000c: stloc.0 
    L_000d: nop 
    L_000e: ldloc.0 
    L_000f: callvirt instance void [System.Data]System.Data.Common.DbConnection::Open()
    L_0014: nop 
    L_0015: nop 
    L_0016: leave.s L_0028
    L_0018: ldloc.0 
    L_0019: ldnull 
    L_001a: ceq 
    L_001c: stloc.1 
    L_001d: ldloc.1 
    L_001e: brtrue.s L_0027
    L_0020: ldloc.0 
    L_0021: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0026: nop 
    L_0027: endfinally 
    L_0028: nop 
    L_0029: nop 
    L_002a: leave.s L_002f
    L_002c: nop 
    L_002d: nop 
    L_002e: endfinally 
    L_002f: nop 
    L_0030: ret 
    .try L_000d to L_0018 finally handler L_0018 to L_0028
    .try L_0001 to L_002c finally handler L_002c to L_002f
}
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:

Select allOpen in new window



Well, the IL code size skyrockets!

Pros for using the “using” word:
  • Neat readable code
  • Less C# code for the human to write
  • No chance of the object not disposing


Pros for manually putting in the graft to think things though:
  • More C# Code
  • Smaller and faster app (we are talking nanoseconds)


So I guess you will have to draw your own conclusions here. In part 2 we will discuss creating our own objects which implement IDisposable, and why and when we should do that.

This article was originally posted at www.zadeveloper.com
Asked On
2010-07-15 at 09:36:51ID3418
Tags

IDisposable

,

C#

,

using keyword

Topic

C# Programming Language

Views
1312

Comments

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top C# Experts

  1. kaufmed

    566,376

    Sage

    500 points yesterday

    Profile
    Rank: Genius
  2. BuggyCoder

    240,764

    Guru

    10 points yesterday

    Profile
    Rank: Sage
  3. navneethegde

    158,560

    Guru

    0 points yesterday

    Profile
    Rank: Wizard
  4. CodeCruiser

    140,708

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  5. TheLearnedOne

    137,350

    Master

    2,800 points yesterday

    Profile
    Rank: Savant
  6. wdosanjos

    124,511

    Master

    3,500 points yesterday

    Profile
    Rank: Genius
  7. AndyAinscow

    107,357

    Master

    0 points yesterday

    Profile
    Rank: Genius
  8. Dhaest

    98,335

    Master

    0 points yesterday

    Profile
    Rank: Genius
  9. Idle_Mind

    91,914

    Master

    0 points yesterday

    Profile
    Rank: Savant
  10. tommyBoy

    90,068

    Master

    0 points yesterday

    Profile
    Rank: Genius
  11. nishantcomp2512

    89,000

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  12. MlandaT

    86,553

    Master

    0 points yesterday

    Profile
    Rank: Genius
  13. Chinmay_Patel

    80,818

    Master

    0 points yesterday

    Profile
    Rank: Genius
  14. ddayx10

    66,538

    Master

    0 points yesterday

    Profile
    Rank: Sage
  15. anarki_jimbel

    66,132

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  16. ambience

    63,764

    Master

    0 points yesterday

    Profile
    Rank: Sage
  17. ukerandi

    62,593

    Master

    1,000 points yesterday

    Profile
    Rank: Guru
  18. apeter

    60,772

    Master

    0 points yesterday

    Profile
    Rank: Sage
  19. JamesBurger

    60,305

    Master

    0 points yesterday

    Profile
    Rank: Sage
  20. sedgwick

    52,750

    Master

    1,600 points yesterday

    Profile
    Rank: Genius
  21. emoreau

    50,917

    Master

    0 points yesterday

    Profile
    Rank: Genius
  22. ged325

    50,311

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  23. anuradhay

    49,977

    2,500 points yesterday

    Profile
    Rank: Guru
  24. techChallenger1

    47,638

    0 points yesterday

    Profile
    Rank: Guru
  25. mas_oz2003

    43,540

    0 points yesterday

    Profile
    Rank: Genius

Hall Of Fame