不能将基类型的变量作为out参数传递?

刚注意到这不起作用:

var dict = new Dictionary(); XContainer element; //... if (dict.TryGetValue(idx, out element)) { //... 

然后我尝试了这个:

 class A { } class B : A { } class Program { static void Main() { A a; a = Ret(); // no error, ok Ref(ref a); // compiler error, ok... Out(out a); // compiler error, lolwut?! } static B Ret() { return null; } static void Ref(ref B b) { } static void Out(out B b) { b = null; } } 

为什么我在上次调用时遇到编译器错误?

编辑:好的,所以我从答案中了解’out’是伪装的’ref’,所以它可以被其他函数或线程共享和更改。 但实际上,不是’out’应该是从函数返回多个值的方法吗? 因为如果是这样的话,它似乎并不擅长。 如果共享产生问题,则不要共享。 只需在函数的开头创建一个隐藏变量,然后使用它。 我的意思是:

 static void Out(out B b) { B bHidden; // compiler generated; // all references to b are replaced with bHidden; b = bHidden; } 

有什么理由不能这样做吗? 现在对我来说似乎很安全……

正如我从答案中所理解的那样’out’是伪装的’ref’,因此它可以被其他函数或线程共享和更改。 但实际上,不是’out’应该是从函数返回多个值的方法吗? 因为如果是这样的话,它似乎并不擅长。 如果共享产生问题,则不要共享。 只需在函数的开头创建一个隐藏变量,然后使用它。 我的意思是:

 static void Out(out B b) { B bHidden; // compiler generated; // all references to b are replaced with bHidden; b = bHidden; } 

有什么理由不能这样做吗? 现在对我来说似乎很安全……

出于显而易见的原因,这种系统被称为“复制”系统。 它可以这样做,但这样做会产生自己的有趣问题。 例如:

 void M() { int b = 1; try { N(out b); } catch (FooException) { Console.WriteLine(b); } } void N(out int c) { c = 123; P(); c = 456; } void P() { throw new FooException(); } 

该计划的输出是什么? 它应该是什么?

下一个问题:您是否希望out的行为与ref一致或不一致? 如果你想让它不一致,那么恭喜你,你刚刚为语言添加了一个非常令人困惑的不一致。 如果你想让它保持一致,那么你需要让ref使用“copy in copy out”语义,这在性能和正确性方面都会引入许多问题。

我可以一整天都在列举引用语义和复制语义之间的差异,但我不会。 我们得到的系统是我们已经拥有的系统,所以要学会使用它。

此外,如果要从方法返回多个值, 请不要使用out参数 。 这可能是2001年的明智之举,但现在是2012年,我们有更多工具可供您使用。 如果要返回两个值:

  • 返回一个元组
  • 将代码重构为两个方法,每个方法返回一个值
  • 如果两个值是值类型和bool,则返回可为空的值类型。

Eric Lippert撰写了这篇文章: http : //blogs.msdn.com/b/ericlippert/archive/2009/09/21/why-do-ref-and-out-parameters-not-allow-type-variation.aspx

修改你的例子:

 class A { } class B : A { public int x; } class Program { static void Main() { A a; a = Ret(); Out(out a, () => a = new A()); } static B Ret() { return null; } static void Ref(ref B b) { } static void Out(out B b, Action callback) { b = new B(); callback(); bx = 3; // cannot possibly work, b is an a again! } } 

C#规范表明类型必须完全匹配

17.5.1.3输出参数

当forms参数是输出参数时,方法调用中的相应参数应包含关键字out,后跟与forms参数相同类型的变量引用(第12.3.3.27节)。

如果允许,您可以这样做:

 class A { } class B : A { public void BOnlyMethod() { } } class C : A { } public class Violations { private A a; public void DoIt() { Violate(out this.a); } void Violate(out B b) { b = new B(); InnocentModification(); // what we think is B, is now C in fact, yet we still can do this: b.BOnlyMethod(); // which is bound to fail, as BOnlyMethod is not present on type C } void InnocentModification() { this.a = new C(); } } 

如果没有这样的限制,违反上述类型系统的行为将太容易实现。 而且我想你不希望用你的语言这种“可能性”。

问题基本上是:不是a = Ret()Out(out a)应该在逻辑上等价吗? 如果是这样,为什么一个有效,另一个没有?

如果我理解正确,CLR实际上没有,而是使用ref ,这意味着幕后Out(out a)被实现为Out(ref a) ,由于显而易见的原因而失败。