注意到这不起作用:
var dict = new Dictionary<int, XElement>();
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;
}
有什么理由不能这样做吗?现在对我来说似乎很安全......
答案 0 :(得分:3)
正如我从答案中所理解的那样'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年,我们有更多工具可供您使用。如果要返回两个值:
答案 1 :(得分:2)
C#规范表明类型必须完全匹配:
17.5.1.3输出参数
当形式参数是输出参数时,方法调用中的相应参数应包含关键字out,后跟与形式参数相同类型的变量引用(第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();
}
}
如果不存在此类限制,违反上述类型系统的行为将太容易实现。而且我想你不希望用你的语言来表达这种“可能性”。
答案 2 :(得分:2)
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();
b.x = 3; // cannot possibly work, b is an a again!
}
}
答案 3 :(得分:1)
问题基本上是:a = Ret()
和Out(out a)
不应该在逻辑上等同吗?如果是这样,为什么一个有效,另一个无效?
如果我理解正确,CLR实际上没有out
,而是使用ref
,这意味着幕后Out(out a)
实现为Out(ref a)
,但失败了原因很明显。