Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / November 2006

Tip: Looking for answers? Try searching our database.

try...catch and local variables

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
RickHodder - 21 Nov 2006 16:50 GMT
I'm getting frustrated with using try...catch with local variables:

The code below wont compile in .NET 1.1: I get the following error: "Use of
unassigned local variable 'oProcessFileReader' "

Is there a way around this error?

<code>
private void Test(string sFileName)
{
    StreamReader oProcessFileReader;
    try
    {                       
        oProcessFileReader  = File.OpenText(sFileName);
    }
    catch (Exception e)
    {
        MessageBox.Show("Error: "+e.Message);
    }
    finally
    {
        if(oProcessFileReader!=null)
            oProcessFileReader.Close();
    }
}

</code>

Signature

Thanks
Rick Hodder

Peter Bromberg [C# MVP] - 21 Nov 2006 16:55 GMT
StreamReader oProcessFileReader=null;

Now it's not unassigned.

Peter

Signature

Co-founder, Eggheadcafe.com developer portal:
http://www.eggheadcafe.com
UnBlog:
http://petesbloggerama.blogspot.com

> I'm getting frustrated with using try...catch with local variables:
>
[quoted text clipped - 23 lines]
>
> </code>
RickHodder - 21 Nov 2006 16:58 GMT
Thanks Peter!

BTW, I really enjoy your blog.
Signature

Rick Hodder

> StreamReader oProcessFileReader=null;
>
[quoted text clipped - 29 lines]
> >
> > </code>
Peter Bromberg [C# MVP] - 21 Nov 2006 18:23 GMT
You're welcome, thanks for the compliment. Isn't it fun to see how 10 people
all say the same thing!
Peter
Signature

Co-founder, Eggheadcafe.com developer portal:
http://www.eggheadcafe.com
UnBlog:
http://petesbloggerama.blogspot.com

> Thanks Peter!
>
[quoted text clipped - 33 lines]
> > >
> > > </code>
Otis Mukinfus - 22 Nov 2006 12:39 GMT
>You're welcome, thanks for the compliment. Isn't it fun to see how 10 people
>all say the same thing!
>Peter

Amusing... (Your second sentence and all the answers) ;o)

Brevity is next to godliness....

Good luck with your project,

Otis Mukinfus
http://www.arltex.com
http://www.tomchilders.com
Mark Rae - 22 Nov 2006 20:52 GMT
> Brevity is next to godliness....

But only in a really bad dictionary... ;-)
Dustin Campbell - 21 Nov 2006 16:55 GMT
> I'm getting frustrated with using try...catch with local variables:
>
> The code below wont compile in .NET 1.1: I get the following error:
> "Use of unassigned local variable 'oProcessFileReader' "
>
> Is there a way around this error?

Set oProcessFileReader to null in your catch block.

Best Regards,
Dustin Campbell
Developer Express Inc.
Dustin Campbell - 21 Nov 2006 18:01 GMT
>> I'm getting frustrated with using try...catch with local variables:
>>
[quoted text clipped - 4 lines]
>>
> Set oProcessFileReader to null in your catch block.

Sorry, I must have been out to lunch when I wrote. Initialize oProcessFileReader
to null when you declare it.

Best Regards,
Dustin Campbell
Developer Express Inc
Mythran - 21 Nov 2006 17:05 GMT
> I'm getting frustrated with using try...catch with local variables:
>
[quoted text clipped - 24 lines]
>
> </code>

By just looking at the code and error, I see you are in fact not assigning
oProcessFileReader before using it.  Even though you are checking for null,
the compiler isn't smart enough to notice and it results in the error you
are getting.  What you can do is simply modify your declaration to read:

StreamReader oProcessFileReader = null;

and it should compile nicely (unless there are other errors in your code
that I'm over-looking.

Note:  This is simply a compiler error and I'm not sure if you can turn off
this functionality.  It exists to prevent you from doing something more
like:

StreamReader reader;
reader.ReadLine();

Which is obviously bad, so the compiler warns/throws a tantrum when you try
to do something that even remotely resembles this...

HTH,
Mythran
Sericinus hunter - 21 Nov 2006 17:14 GMT
...
> Note:  This is simply a compiler error and I'm not sure if you can turn
> off this functionality.  It exists to prevent you from doing something
[quoted text clipped - 5 lines]
> Which is obviously bad, so the compiler warns/throws a tantrum when you
> try to do something that even remotely resembles this...

   Do you mean that |reader| in your example is null? I am not sure
if memory for local variable declarations is primed with zeros. Is it
somewhere in the language specifications?
Marc Gravell - 21 Nov 2006 17:27 GMT
> I am not sure if memory for local variable
> declarations is primed with zeros. Is it
> somewhere in the language specifications?

ECMA-334, §12 "A variable shall be definitely assigned (§12.3) before its
value can be obtained."

So within the language, it is irrelevant (an implementation detail) whether
the variable is zero'd - as you cannot legally look to see. The CLR may
behave differently, but since this is a C# NG...

Marc
Sericinus hunter - 21 Nov 2006 17:34 GMT
>> I am not sure if memory for local variable
>> declarations is primed with zeros. Is it
[quoted text clipped - 6 lines]
> the variable is zero'd - as you cannot legally look to see. The CLR may
> behave differently, but since this is a C# NG...

   Then, this is not a compiler error, contrary to what was suggested. Thanks.
Mythran - 21 Nov 2006 17:43 GMT
>>> I am not sure if memory for local variable
>>> declarations is primed with zeros. Is it
[quoted text clipped - 9 lines]
>    Then, this is not a compiler error, contrary to what was suggested.
> Thanks.

My apologies, it should read, "this is a compile* error" not compiler and
disregard the "I'm not sure if you can turn off this functionality", cause
obviously, you can't...

:)

HTH,
Mythran
Peter Duniho - 21 Nov 2006 23:42 GMT
> By just looking at the code and error, I see you are in fact not assigning
> oProcessFileReader before using it.  Even though you are checking for
> null, the compiler isn't smart enough to notice and it results in the
> error you are getting.

The compiler does notice that he's checking for null.  The problem is that
he's not allowed to check for null until he's set the variable to null.  He
really does have an uninitialized variable bug in his code.

> [...]
> Note:  This is simply a compiler error and I'm not sure if you can turn
[quoted text clipped - 6 lines]
> Which is obviously bad, so the compiler warns/throws a tantrum when you
> try to do something that even remotely resembles this...

In this particular case, there *is* a code path that can result in the local
variable being accessed before being initialized, without initialization in
the declaration.  Initializing the local variable is indeed the correct
solution to the error, and is also correct relative to the rest of the code
that was posted.

However, unfortunately the compiler sometimes also incorrectly complains
that the variable is uninitialized, even when all code paths *do* initialize
the variable before it's accessed.

IMHO, this is a serious compiler bug, because the only way to get rid of the
error is to put in a "dummy" initialization (such as setting the variable to
null), which consequentially will hide any true "uninitialized variable"
errors in the code.

In other words, with the compiler in the state in which it is now, there are
actually situations in which the compiler forces the programmer to write
code that is *more* likely to contain the bug that the error is trying to
help avoid.  I'd find the irony very entertaining, if it weren't for the
fact that there's a serious code quality issue related to it.  :)

Pete
Mythran - 22 Nov 2006 01:12 GMT
>> By just looking at the code and error, I see you are in fact not
>> assigning oProcessFileReader before using it.  Even though you are
[quoted text clipped - 38 lines]
>
> Pete

Yes yes, what I was thinking and what I typed were two diff things...

The compiler/framework isn't smart enough to see that it's just checking to
see that it's null...not really "smart enough", but wasn't written to work
that way :)

I wonder, and it's not tested...

StreamReader sr;
if (true) { sr = ... }
if (sr == null) { ... }

I wonder if the compiler/framework will barf on that too?  :)  Most
likely...

Maybe

HTH,
Mythran
Peter Duniho - 22 Nov 2006 01:24 GMT
> Yes yes, what I was thinking and what I typed were two diff things...

Is that still happening.  :)

> The compiler/framework isn't smart enough to see that it's just checking
> to see that it's null

Checking to see that a variable is null is *accessing* that variable.  If
the variable has not been initialized, then checking for null (or any other
value) is illegal.

This isn't an issue of "smart enough".  The error in this case is valid and
correct.  The code *is* actually buggy, as the variable is being used at a
point in time at which it's undefined.

> ...not really "smart enough", but wasn't written to work that way :)

In this particular example, the compiler is doing exactly what it's supposed
to.

> I wonder, and it's not tested...
>
[quoted text clipped - 4 lines]
> I wonder if the compiler/framework will barf on that too?  :)  Most
> likely...

That is more like the compiler bug I was talking about.  I haven't tried
that exact construct, but I have written code where it is provable that the
variable *is* initialized prior to access, and yet the compiler complains
that it isn't.  In these cases (such as your example, and the other examples
I've seen), adding the initialization required to get the compiler to stop
complaining can actually result in what is effectively an uninitialized
value bug, because the otherwise-unnecessary initialization hides any other
problems with initialization.

Pete
Jon Skeet [C# MVP] - 22 Nov 2006 20:28 GMT
<snip>

> > I wonder if the compiler/framework will barf on that too?  :)  Most
> > likely...
[quoted text clipped - 7 lines]
> value bug, because the otherwise-unnecessary initialization hides any other
> problems with initialization.

It's not a compiler bug. The compiler is working exactly to the
language specification, as it should do.

Now, if you want to complain that the language specification is
"wrong" - the specification is already fairly complicated in terms of
working out precisely when a variable is definitely assigned. I believe
it is much better to make a language slightly "dumber" but simpler to
reason about (i.e. simpler to make sure the compiler is correct,
simpler to make sure the specification doesn't let some oddities
through etc) than to try to make it incredibly smart.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Peter Duniho - 22 Nov 2006 20:42 GMT
> It's not a compiler bug. The compiler is working exactly to the
> language specification, as it should do.

I don't have a copy of the language specification.  Would you please quote
the part you're referencing, where the language specification says that even
if a statement is reachable only by code paths that initialize the variable,
the compiler should still emit an "uninitialized variable" error.

Thanks,
Pete
Jon Skeet [C# MVP] - 22 Nov 2006 22:39 GMT
> > It's not a compiler bug. The compiler is working exactly to the
> > language specification, as it should do.
[quoted text clipped - 3 lines]
> if a statement is reachable only by code paths that initialize the variable,
> the compiler should still emit an "uninitialized variable" error.

From the C# 1.1 ECMA spec:

http://www.jaggersoft.com/csharp_standard/12.3.htm
and the pages following (click on the arrows on the right)

(I don't have a public link to a 2.0 spec handy.)

Those rules define the whether or not something is definitely assigned.
They don't say anything about things which the compiler *could* reason
about, such as:

string x;
for (int i=0; i < 10; i++)
{
   // 0 < 10 so this will always be executed exactly
   // once
   x = "value";
}
Console.WriteLine (x);

and

string x;
if (true)
{
   // This will definitely be executed
   x = "value";
}
Console.WriteLine (x);

However, there *is* enough reasoning (because it has nothing to do with
the expressions within the statements) for this to be okay:

string x;
if (something)
{
   x = "value";
}
else
{
   x = "othervalue";
}
Console.WriteLine (x);

http://www.jaggersoft.com/csharp_standard/12.htm states that a variable
must be definitely assigned before its value can be obtained.

Any compiler which *did* allow either of the first two snippets above
to be compiled without error would be violating the spec.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Peter Duniho - 22 Nov 2006 23:19 GMT
> [...]
> Any compiler which *did* allow either of the first two snippets above
> to be compiled without error would be violating the spec.

I don't understand why that is the case.  The page you referred me to simply
says "a variable is said to be definitely assigned if the compiler can
prove, by static flow analysis, that the variable has been automatically
initialized or has been the target of at least one assignment".

Since it is possible by static flow analysis (that is, by analyzing the flow
of execution in a non-runtime situation) to show that both of the examples
you mention will definitely assign the variable, why is the variable still
considered unassigned?

Pete
Marc Gravell - 23 Nov 2006 06:18 GMT
It does spell out the official rules in great depth; 12.3.3.9 states
that "for" is treated as "while"; "while" is described in 12.3.3.7:
http://www.jaggersoft.com/csharp_standard/12.3.3.7.htm

This basically gives a few rules about what happens if the loop test
(typically e.g. "i<10" in "for") definitely assigns a variable, but
says nothing about the body. So that is what any compliant compiler
should do...

Marc
Peter Duniho - 23 Nov 2006 06:51 GMT
> It does spell out the official rules in great depth; 12.3.3.9 states
> that "for" is treated as "while"; "while" is described in 12.3.3.7:
[quoted text clipped - 4 lines]
> says nothing about the body. So that is what any compliant compiler
> should do...

Well, I will admit that I have no special insight into the intent of the
writers of the specification.  However, my interpretation is that the
specification "says nothing about the body" because the body is itself a
statement, and the rules for that statement would be described elsewhere,
depending on the form it takes.

It's the standard recursive grammar sort of thing.

And in particular, static analysis can show in that situation that the body
statement *is* reached, and *does* initialize the variable.

So, I still don't see anything in the specification that says that even when
a statement is only reachable by code paths that initialize a variable, the
compiler should emit an "uninitialized variable" error.

Pete
Peter Duniho - 23 Nov 2006 06:57 GMT
> And in particular, static analysis can show in that situation that the
> body statement *is* reached, and *does* initialize the variable.

Clarification:

"is *always* reached".  That is, there is no code path that avoids the body
statement.
Jon Skeet [C# MVP] - 23 Nov 2006 08:59 GMT
> Well, I will admit that I have no special insight into the intent of the
> writers of the specification.  However, my interpretation is that the
> specification "says nothing about the body" because the body is itself a
> statement, and the rules for that statement would be described elsewhere,
> depending on the form it takes.

The rules for definite assignment are all laid out under the section I
pointed you at - but they're not all on one page. That's why I
explained that you should use the arrows on the right of the page to
see the rest of the details.

<snip>

> So, I still don't see anything in the specification that says that even when
> a statement is only reachable by code paths that initialize a variable, the
> compiler should emit an "uninitialized variable" error.

I have to admit to a mistake earlier - section 15.1 of the spec is
relevant here:
http://www.jaggersoft.com/csharp_standard/15.1.htm

In particular:

int x;
if (true)
{
   x=0;
}
Console.WriteLine (x);

*is* valid, because the if expression is a *constant* one. The
following is invalid, because the compiler doesn't do any reasoning
about possible values of variables:

int x;
bool y = true;
if (y)
{
   x=0;
}
Console.WriteLine (x);

In other words, the compiler does some *very primitive* checking. For
the "if" statement, the rules for reachability are described in
http://www.jaggersoft.com/csharp_standard/15.7.1.htm

Hope this clears things up - apologies for the previous inaccuracy
about the "if" example.

Jon
Willy Denoyette [MVP] - 23 Nov 2006 09:42 GMT
>> > It's not a compiler bug. The compiler is working exactly to the
>> > language specification, as it should do.
[quoted text clipped - 10 lines]
>
> (I don't have a public link to a 2.0 spec handy.)

Here it is:
http://www.ecma-international.org/publications/standards/Ecma-334.htm

Willy.
Mark R. Dawson - 21 Nov 2006 17:34 GMT
Another way to write your code would be to use the "using" statement.  It is
basically under the hood a try/finally statement which calls the Dispose
method on an object that implement IDisposable.  If you want your catch
statement you will have to explicitly put that inside the using statment. So
your code:

> private void Test(string sFileName)
> {
[quoted text clipped - 13 lines]
>     }
> }

would become (without the catch statement):

private void Test(string sFileName)
{
    using(StreamReader oProcessFileReader = File.OpenText(sFileName))
    {                       
          //use the oProcessFileReader
               }

               //here the stream has been disposed.
}

So even if an exception is thrown the resources are cleaned up, like in your
code, but with less typing :-).

Mark.
Signature

http://www.markdawson.org

> I'm getting frustrated with using try...catch with local variables:
>
[quoted text clipped - 23 lines]
>
> </code>
Dustin Campbell - 21 Nov 2006 18:03 GMT
> Another way to write your code would be to use the "using" statement.
> It is basically under the hood a try/finally statement which calls the
[quoted text clipped - 31 lines]
> So even if an exception is thrown the resources are cleaned up, like
> in your code, but with less typing :-).

Unfortunately, a using statement doesn't actually protect the code that was
protected before. It will compile to this:

StreamReader oProcessFileReader = File.OpenText(sFileName);
try
{
 // use the oProcessFileReader
}
finally
{
 if (oProcessFileReader != null)
   ((IDisposable)oProcessFileReader).Dispose();
}

So, if an exception is raised by File.OpenText(), the call stack will immediately
unwind and the try/finally code doesn't ever execute. IOW, the code that
you presented doesn't actually do the same thing. I suppose that you *could*
do this:

private void Test(string sFileName)
{
 try
 {
   using (StreamReader oProcessFileReader = File.OpenText(sFileName))
   {                        
     //use the oProcessFileReader
   }

   //here the stream has been disposed.
 }
 catch (Exception e)
 {
   MessageBox.Show("Error: " + e.Message);
 }
}

But, now the scope for try/catch is pretty different--it will also be catching
any exceptions thrown by code other than File.OpenText.

I don't think there's anyway to precisely re-write the original code with
a using statement.

Best Regards,
Dustin Campbell
Developer Express Inc.
Mark R. Dawson - 21 Nov 2006 22:20 GMT
You are correct, I should have looked more closely at the OP.  Note to self,
do not post when running out the door :-)

> Unfortunately, a using statement doesn't actually protect the code that was
> protected before. It will compile to this:
[quoted text clipped - 9 lines]
>     ((IDisposable)oProcessFileReader).Dispose();
> }

Also there are two extra braces that get compiled by the using statement to
limit the scope the of the local variable:

{
 StreamReader oProcessFileReader = File.OpenText(sFileName);
 try
 {
   // use the oProcessFileReader
 }
 finally
 {
   if (oProcessFileReader != null)
    ((IDisposable)oProcessFileReader).Dispose();
  }
}

Mark.
Signature

http://www.markdawson.org

> > Another way to write your code would be to use the "using" statement.
> > It is basically under the hood a try/finally statement which calls the
[quoted text clipped - 77 lines]
> Dustin Campbell
> Developer Express Inc.
Adrian Gallero - 22 Nov 2006 00:10 GMT
Hi Dustin,

This is just for the fun of it, but I think your code:

try
{
  using (StreamReader oProcessFileReader = File.OpenText(sFileName))
 {                              //use the oProcessFileReader
 }

  //here the stream has been disposed.
}
catch (Exception e)
{
  MessageBox.Show("Error: " + e.Message);
}

is very similar in functionality to the original code. (with some scope
issues aside, because oProcessFileReader is not accessible now outside
the using block)

>But, now the scope for try/catch is pretty different--it will also be
>catching any exceptions thrown by code other than File.OpenText.

On the original code, as it was inside a try/catch/finally block (and I
am still not sure what try/catch/finally blocks are useful for), if he
wanted to do anything with the stream, it would also catch those
exceptions, as your code does:

    StreamReader oProcessFileReader = null;
    try
    {                       
        oProcessFileReader  = File.OpenText(sFileName);
          //Do whatever you want to do with the stream here.
          //Note that an exception here will be processed on the
          //catch statement.
    }
    catch (Exception e)
    {
        MessageBox.Show("Error: "+e.Message);
    }
    finally
    {
        if(oProcessFileReader!=null)
            oProcessFileReader.Close();
    }

Regards,
 Adrian.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.