try...catch and local variables

G

Guest

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

Dustin Campbell

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

Mythran

RickHodder said:
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>

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
 
S

Sericinus hunter

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

Marc Gravell

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
 
G

Guest

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

Sericinus hunter

Marc said:
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...

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

Mythran

Sericinus hunter said:
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
 
D

Dustin Campbell

I'm getting frustrated with using try...catch with local variables:
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
 
D

Dustin Campbell

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:

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.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
G

Guest

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

Peter Duniho

Mythran said:
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
 
A

Adrian Gallero

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

Mythran

Peter Duniho said:
Mythran said:
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

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
 
P

Peter Duniho

Mythran said:
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
 
J

Jon Skeet [C# MVP]

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Variables and Try/Catch 14
try & catch 8
try { DataReader } catch { 22 } 9
try-catch question 5
Once again on try/catch, using and IO 7
Unassigned error in Catch 3
try...catch...inside loop 8
C# socket reconnect 3

Top