2012-01-18 53 views
27

说我有下面的代码:“fjuk”输出参数和异常

static void Fjuk(out string str) 
    { 
     str = "fjuk!"; 
     throw new Exception(); 
    } 

    static void Main(string[] args) 
    { 
     string s = null; 
     try 
     { 
      Fjuk(out s); 
     } 
     catch (Exception) 
     { 
      Console.WriteLine(s ?? ""); 
     } 
    } 

当我测试了一下,s已经被初始化到当它在catch区块中使用时。
这是由规范保证还是依赖于实现? (我已经搜查了C#3.0规范,但无法找到自己)

+0

我不知道规范,但它肯定是我期望的。我期望成员变量,属性等的初始化也将在你的catch块中可用。 – 2012-01-18 07:57:30

+1

Eric Lippert在什么时候需要他...... :) – 2012-01-18 08:00:52

+0

@jb。 [MSDN]有什么问题(http://msdn.microsoft.com/en-us/library/t3c3bfhx(v = vs.80).aspx)? – gdoron 2012-01-18 08:08:59

回答

23

差不多,即什么out手段一个方面;首先,请注意out并不存在 - 我们只需要考虑refout仅仅是ref,在编译器中有一些“明确的分配”调整)。 ref的意思是“传递这个地址” - 如果我们通过地址更改值,那么立即显示 - 它毕竟是更新内存的堆栈Main。它不能抽象这个(延迟写入),因为这个值可能是,例如,一些特别用于避免将其复制到堆栈上的超大结构(在XNA等中广泛使用的一种方法)。

5

它的“保证”因为out参数改变与参数的memory address值。

out关键字导致参数通过引用传递。这与ref关键字类似,除了ref要求变量在被传递之前被初始化。

MSDN

4

如果该方法抛出异常,则不保证输出参数被设置。如果该方法没有异常退出,则保证输出参数被设置。

在你的情况下,该方法将始终设置输出参数,但编译器不会以这种方式分析该方法的代码。如果该方法退出并出现异常,则输出参数仍然不被认为是明确设置的。

您在异常处理程序中的代码不依赖于由方法调用设置的变量,因为您在创建变量时设置该变量。如果您在创建时没有设置变量,异常处理程序不能使用它,因为它不能保证设置:

string s; 
try { 
    Fjuk(out s); 
    Console.WriteLine(s); // here the variable is guaranteed to be set 
} catch (Exception) { 
    Console.WriteLine(s); // here it's not, so this won't compile 
} 
+0

是的,我认为它很明显,它不检查被调用方法中的代码,但没有在我的例子中说清楚。感谢您指出了这一点! – Niklas 2012-01-18 11:36:39

1

它从Fjuk角度保证,但不是Main

Fjuk设置参数后抛出异常。尽管可以通过编译器,抖动和CPU完成重新排序,但不会有重新排序,以致单个线程观察到的顺序发生变化。由于单个线程可能会“注意到”在抛出异常之前未设置参数,因此参数将保证设置。

尽管在Main中,我们并不知道Fjuk的实现细节,所以当编译器分析Main时,它不能依赖于此。因此,在变化,我们不调用之前的值赋给s

static void Main() 
{ 
    string s; 
    try 
    { 
     Fjuk(out s); 
     Console.WriteLine(s ?? "");//fine 
    } 
    catch (Exception) 
    { 
     Console.WriteLine(s ?? "");//compiler error 
    } 
    Console.WriteLine(s ?? "");//compiler error 
} 

使用s后马上打电话给Fjuk是好的,因为一次只能到达那里,如果Fjuk成功的第一次尝试,如果Fjuk成功,则必须分配s。然而,在第二和第三种情况下,可以在没有Fjuk成功的情况下到达那些行,并且由于无法通过分析Main分析是否可以在设置s之前抛出异常,因此必须禁止使用s