Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

try...catch and local variables

1 view
Skip to first unread message

RickHodder

unread,
Nov 21, 2006, 11:50:02 AM11/21/06
to
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>


--
Thanks
Rick Hodder

Peter Bromberg [C# MVP]

unread,
Nov 21, 2006, 11:55:01 AM11/21/06
to
StreamReader oProcessFileReader=null;

Now it's not unassigned.

Peter


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

Dustin Campbell

unread,
Nov 21, 2006, 11:55:36 AM11/21/06
to
> 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.


RickHodder

unread,
Nov 21, 2006, 11:58:02 AM11/21/06
to
Thanks Peter!

BTW, I really enjoy your blog.
--
Rick Hodder

Mythran

unread,
Nov 21, 2006, 12:05:18 PM11/21/06
to

"RickHodder" <RickH...@discussions.microsoft.com> wrote in message
news:F0474355-35FC-421A...@microsoft.com...

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

unread,
Nov 21, 2006, 12:14:46 PM11/21/06
to
Mythran wrote:
...

> 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...

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

unread,
Nov 21, 2006, 12:27:38 PM11/21/06
to
> 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

unread,
Nov 21, 2006, 12:34:29 PM11/21/06
to

Then, this is not a compiler error, contrary to what was suggested. Thanks.

Mark R. Dawson

unread,
Nov 21, 2006, 12:34:01 PM11/21/06
to
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)
> {
> StreamReader oProcessFileReader;
> try
> {
> oProcessFileReader = File.OpenText(sFileName);
> }
> catch (Exception e)
> {
> MessageBox.Show("Error: "+e.Message);
> }
> finally
> {
> if(oProcessFileReader!=null)
> oProcessFileReader.Close();
> }
> }

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.
--
http://www.markdawson.org

Mythran

unread,
Nov 21, 2006, 12:43:04 PM11/21/06
to

"Sericinus hunter" <ser...@flash.net> wrote in message
news:#T$aPNZDH...@TK2MSFTNGP02.phx.gbl...

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

Dustin Campbell

unread,
Nov 21, 2006, 1:01:02 PM11/21/06
to
>> 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.

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

Dustin Campbell

unread,
Nov 21, 2006, 1:03:59 PM11/21/06
to
> 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)
>> {
>> StreamReader oProcessFileReader;
>> try
>> {
>> oProcessFileReader = File.OpenText(sFileName);
>> }
>> catch (Exception e)
>> {
>> MessageBox.Show("Error: "+e.Message);
>> }
>> finally
>> {
>> if(oProcessFileReader!=null)
>> oProcessFileReader.Close();
>> }
>> }
> 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 :-).

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.

Peter Bromberg [C# MVP]

unread,
Nov 21, 2006, 1:23:02 PM11/21/06
to
You're welcome, thanks for the compliment. Isn't it fun to see how 10 people
all say the same thing!

Mark R. Dawson

unread,
Nov 21, 2006, 5:20:02 PM11/21/06
to
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:
>
> StreamReader oProcessFileReader = File.OpenText(sFileName);
> try
> {
> // use the oProcessFileReader
> }
> finally
> {
> if (oProcessFileReader != null)
> ((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.
--
http://www.markdawson.org

Peter Duniho

unread,
Nov 21, 2006, 6:42:42 PM11/21/06
to
"Mythran" <kip_p...@hotmail.com> wrote in message
news:uZ5b58YD...@TK2MSFTNGP02.phx.gbl...

> 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
> 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...

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


Adrian Gallero

unread,
Nov 21, 2006, 7:10:00 PM11/21/06
to
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.

Mythran

unread,
Nov 21, 2006, 8:12:24 PM11/21/06
to

"Peter Duniho" <NpOeS...@NnOwSlPiAnMk.com> wrote in message
news:12m73o2...@corp.supernews.com...

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

unread,
Nov 21, 2006, 8:24:55 PM11/21/06
to
"Mythran" <kip_p...@hotmail.com> wrote in message
news:%23X7QENd...@TK2MSFTNGP02.phx.gbl...

> 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...
>
> StreamReader sr;
> if (true) { sr = ... }
> if (sr == null) { ... }
>
> 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


Otis Mukinfus

unread,
Nov 22, 2006, 7:39:11 AM11/22/06
to
On Tue, 21 Nov 2006 10:23:02 -0800, Peter Bromberg [C# MVP]
<pbro...@yahoo.nospammin.com> wrote:

>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

Jon Skeet [C# MVP]

unread,
Nov 22, 2006, 3:28:11 PM11/22/06
to
Peter Duniho <NpOeS...@NnOwSlPiAnMk.com> wrote:

<snip>

> > 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.

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.

--
Jon Skeet - <sk...@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

unread,
Nov 22, 2006, 3:42:02 PM11/22/06
to
"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
news:MPG.1fcec3369...@msnews.microsoft.com...

> 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


Mark Rae

unread,
Nov 22, 2006, 3:52:25 PM11/22/06
to
"Otis Mukinfus" <ph...@emailaddress.com> wrote in message
news:j4h8m2l1g7ntapssf...@4ax.com...

> Brevity is next to godliness....

But only in a really bad dictionary... ;-)


Jon Skeet [C# MVP]

unread,
Nov 22, 2006, 5:39:33 PM11/22/06
to

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.

Peter Duniho

unread,
Nov 22, 2006, 6:19:04 PM11/22/06
to
"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
news:MPG.1fcee200c...@msnews.microsoft.com...
> [...]

> 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

unread,
Nov 23, 2006, 1:18:21 AM11/23/06
to
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

unread,
Nov 23, 2006, 1:51:59 AM11/23/06
to
"Marc Gravell" <marc.g...@gmail.com> wrote in message
news:1164262701.4...@m7g2000cwm.googlegroups.com...

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

unread,
Nov 23, 2006, 1:57:29 AM11/23/06
to
"Peter Duniho" <NpOeS...@NnOwSlPiAnMk.com> wrote in message
news:12mahaa...@corp.supernews.com...

> 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]

unread,
Nov 23, 2006, 3:59:56 AM11/23/06
to
Peter Duniho wrote:
> 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]

unread,
Nov 23, 2006, 4:42:34 AM11/23/06
to
"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
news:MPG.1fcee200c...@msnews.microsoft.com...

> Peter Duniho <NpOeS...@NnOwSlPiAnMk.com> wrote:
>> "Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
>> news:MPG.1fcec3369...@msnews.microsoft.com...
>> > 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.
>
> 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.)
>

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


Willy.

0 new messages