C# 为什么try块中的代码与方法的其余部分是分开的?

C# 为什么try块中的代码与方法的其余部分是分开的?,c#,exception-handling,C#,Exception Handling,我的问题是这样的: 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;

我的问题是这样的:

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();
HttpWebRequest request = null;
我将从中得到的错误是,
request
没有被赋予一个值。显然,这是因为请求的值是在try块中给出的

这让我感到困惑的原因是,在我使用的其他语言中,
try
块中的代码没有与代码的其余部分分开(我忘记了这个词,可能是封装?),这与方法类似


我走错方向了吗?如果
WebRequest
没有抛出异常,我是否应该在异常后复制try块中的代码?

您误解了错误

request
变量在所有代码的范围内。但是,在
try
块之外,不能保证它有一个值,并且C#编译器将不允许您使用变量,除非它可以确保变量已经被赋值

特别是,如果
WebRequest.Create
抛出异常,
request
将不会被分配到

您可以通过在
catch
块之外指定一个值来修复它,如下所示:

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();
HttpWebRequest request = null;

顺便说一下,您不应该在这里使用
catch
块。

相反,您应该调用。

要解决这个问题,您应该给请求一个默认值,如
HttpWebRequest request=null

C#和大多数C风格的语言(但不是JavaScript!)都有块作用域,这就是您要寻找的术语

因此,通过当前作用域的每个执行路径都应该设置
请求
参数。所以

//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();
将失败,就像在新的作用域中一样,只有通过
a
设置
请求的执行路径。与
相同,请尝试catch
try
catch
都应该设置请求变量


块作用域也非常有用,如下所示:

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

// second-part
{
    string myVariable = "hi"; // is valid
}

如果抛出异常,那么您的请求对象将为null,因此最后一行将以null引用失败

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;
}

由于范围的原因,很遗憾,您可以将第一行

HttpWebRequest request = null;
这将解决它,基本上代码的作用域是try块,因此它之外的任何东西都不会意识到已经设置了值

HttpWebRequest request = null;
try块中的代码位于内部作用域中


当您尝试获取响应时,您需要先检查
HttpWebRequest
是否为空。

您觉得以下各项的外观如何:

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();

否则,您只会得到一个空指针异常,该异常将掩盖原始错误。您可以在声明中将request显式设置为null,然后在使用它时检查它是否为null。

要理解这一点,您首先需要了解一点范围和声明空间:

范围定义了可以使用名称的位置 而宣言空间则侧重于 这个名字是唯一的。范围和 申报空间密切相关, 但也有一些微妙之处 差异

更正式的定义 范围是一个封闭的上下文 或定义名称所在位置的区域 可以在没有资格的情况下使用

在 C#,范围和声明空间 由语句块定义 用大括号围起来。这意味着 名称空间、类、方法和 所有属性都定义范围和范围 声明空间。因此, 作用域可以嵌套并相互重叠 其他的

try块(正确地称为受保护区域)的语言语义是受保护区域定义范围。这意味着在该范围内定义的任何变量在该范围内(以及任何嵌套范围内)仅通过名称可见

接下来您会遇到一个问题,.NET Framework强制执行类型安全的方法之一是不允许使用统一化变量。因此,由于您声明了
HttpWebRequest请求作为局部变量,未提供初始值。此外,由于实际提供值的唯一位置是在受保护区域内,因此编译器“足够聪明”,能够意识到受保护区域内的代码可能不会运行(由于发生异常)它能够验证执行路径是否会导致
请求
从未被赋值,并发出错误

正确的处理方法是使用以下代码:

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(); 
}

是的,响应也应该在try块中设置,+1实际上,它不应该设置
request.GetResponse
无法抛出
UriFormatException
@Slaks。WebRequest.Create将引发异常。当它这样做时,该应用程序将继续在catch块之后运行。要么像@Rob Fonseca Ensor的例子那样结束该方法,要么在创建请求后在try块中检索响应。这样我们就知道我们的请求是正确创建的。可以为GetResponse引发的异常添加其他捕获块,如下所示:正确。但是,它不应位于相同的
try
块中。相反,应该有一个
return
语句。@Slaks我同意在try块中放入一些永远不会抛出预期异常的内容是不好的,但在这种情况下,它可能会产生最少的代码。这是完全错误的。范围不是这里的问题;问题是第一行可能会抛出异常,因此可能根本无法设置值。恐怕我不同意,在他的示例中,在他尝试使用请求的同一范围内,没有为请求设置任何值,这就是为什么设置为null是错误的。范围与明确的分配无关。这里的问题是try块可能在分配发生之前终止。这是不正确的。这里的问题是明确的任务,而不是范围;问题中给出的代码示例与此答案不一致。我认为您要查找的单词是scope.Act