views:

165

answers:

8

My problem goes something like this:

HttpWebRequest request;

try {
    request = (HttpWebRequest) WebRequest.Create(url);
} catch (UriFormatException) {
    statusLabel.Text = "The address you entered was malformed, please correct it.";
    statusLabel.ForeColor = Color.Red;
}

HttpWebResponse response  = (HttpWebResponse) request.GetResponse();

The error I'll get from this is that request hasn't been given a value. Obviously this is because the value for request is given in the try block.

The reason this confuses me is because in other languages I've used, code in a try block isn't kept separate (I forget the word for this, possibly encapsulation?) from the rest of the code - similar to a method.

Am I going about this the wrong way? Should I duplicate the code in the try block after the exception supposing WebRequest doesn't throw one?

+12  A: 

You're misunderstanding the error.

The request variable is in scope for all of the code. However, outside the try block, it is not guaranteed to have a value, and the C# compiler will not allow you to use a variable unless it can be sure that the variable has already been assigned.

Specifically, if WebRequest.Create throws an exception, request will not have been assigned to.

You can fix it by assigning a value outside the catch block, like this:

HttpWebRequest request = null;

By the way, you should not be using a catch block at all here.
Instead, you should call Uri.TryCreate.

SLaks
I understand now - thanks!
Ross
It's actually not that the `request` variable isn't guaranteed to have a value in the `catch` block, but rather that it isn't guaranteed to have a value after the entire protected region (try/catch pair) have executed.
Scott Dorman
@Scott: You're right; I misread the question. Fixed.
SLaks
Good point about Uri.TryCreate +1
Naeem Sarfraz
This is going to cause a confusing error (null pointer) if the UriFormatException is thrown, but +1 for the TryCreate
Rob Fonseca-Ensor
+6  A: 

To solve this you'd give request a default value like HttpWebRequest request = null;.

C# and most C-style languages (but not JavaScript!) have block-scope, that's the term you were looking for.

Therefore every execution path through the current scope should set the request parameter. So

//create new scope (every '{ }' block has it's own scope, so you can also create
// a new one, by just wrapping some code inside accolades.
{
    if(a) request = something;
    else if(b) // do nothing
}

request.DoSomething();

Will fail, as in your new scope, only the execution path that goes through a sets request. Same thing with try-catch. Both try and catch should set the request variable.


Block scope is also quite usefull, like this is valid:

// first-part-of-my-app
{
    int myVariable = 10;
}

// second-part
{
    string myVariable = "hi"; // is valid
}
Jan Jongboom
This is incorrect. The problem here is definite assignment, not scope.
SLaks
I have to agree with SLaks; the code example given in the question disagrees with this answer.
R. Bemrose
+2  A: 

If there's an exception thrown then your request object will be null and so the last line will fail with a null reference.

HttpWebRequest request;

try {
    request = (HttpWebRequest) WebRequest.Create(url);
    HttpWebResponse response  = (HttpWebResponse) request.GetResponse();
    // do stuff with your response
} catch (UriFormatException) {
    statusLabel.Text = "The address you entered was malformed, please correct it.";
    statusLabel.ForeColor = Color.Red;
}
Naeem Sarfraz
true, the response should be set in the try block as well, +1
Pharabus
Actually, it shouldn't. `request.GetResponse` cannot throw a `UriFormatException`.
SLaks
@Slaks. WebRequest.Create will throw the exception. When it does the app will continue to run following the catch block. Either the method should end, as in @Rob Fonseca-Ensor's example, or the response is retrieved in the try block following the creating of the request. That way we know our request was created correctly. Other catch blocks can be added for exceptions that are thrown by GetResponse as listed here: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.getresponse.aspx
Naeem Sarfraz
Correct. However, it should not be in the same `try` block. Instead, there should be a `return` statement.
SLaks
@Slaks i agree that it's bad form to put something in a try block that will never throw the expected exception, but in this case it will probably result in the least amount of code.
Rob Fonseca-Ensor
A: 

it is due to scope unfortunately, you can ammend the first line to

HttpWebRequest request = null;

and that will fix it, basically the code is scoped withing the try block so anything outside it will not be aware a value has been set

Pharabus
This is completely false. Scope is not the issue here; the problem is that the first line might throw an exception, so that value might not be set at all.
SLaks
I am afraid I disagree, in his example no value has been set to the request in the same scope as when he is trying to use it, that is why setting to null works
Pharabus
You are wrong. Scope has nothing to do with definite assignment. The problem here is that the try block may have terminated before the assignment happened.
SLaks
A: 

Just set

HttpWebRequest request = null;

The code in the try block is in an inner scope.

When you try to get your response you will need to check to see if HttpWebRequest is null first.

Dave
+2  A: 

How do you like the look of:

HttpWebRequest request;

try {
    request = (HttpWebRequest) WebRequest.Create(url);
} catch (UriFormatException) {
    statusLabel.Text = "The address you entered was malformed, please correct it.";
    statusLabel.ForeColor = Color.Red;
    return;
}

HttpWebResponse response  = (HttpWebResponse) request.GetResponse();

Otherwise you'll just get a null pointer exception that will mask the original error

Rob Fonseca-Ensor
A: 

You can explicitly set request to null in your declaration and then you can check if it is null when you come to use it.

Robin Day
A: 

To understand this, you first need to understand a little bit about scope and declaration space:

Scope defines where you can use a name while declaration space focuses on where that name is unique. Scope and declaration space are closely related, but there are a few subtle differences.

A more formal definition is that scope is an enclosing context or region which defines where a name can be used without qualification.

In C#, both scope and declaration space is defined by a statement block enclosed by braces. That means namespaces, classes, methods, and properties all define both a scope and a declaration space. As a result, scopes can be nested and overlap each other.

The language semantics for a try block (properly called a protected region) are that the protected region defines a scope. This means that any variables defined inside that scope are only visible by name within that scope (and any nested scopes).

The next thing you run into is that one of the ways the .NET Framework enforces type safety is to disallow unitialized variables. As a result, since you declare HttpWebRequest request; as a local variable it has not been provided an initial value. Further, since the only place that does actually provide a value is inside a protected region, the compiler is "smart enough" to realize that the code inside the protected region might not run (as the result of an exception occurring) it is able to verify that the execution path can result in request never having been assigned a value and issues the error.

The proper way to handle this would be using code like:

HttpWebRequest request = null;

try 
{ 
    request = (HttpWebRequest) WebRequest.Create(url); 
}
catch (UriFormatException)
{ 
    statusLabel.Text = "The address you entered was malformed, please correct it."; 
    statusLabel.ForeColor = Color.Red; 
} 

if (request != null)
{  
   HttpWebResponse response = (HttpWebResponse) request.GetResponse(); 
}
Scott Dorman