我在下面遇到编译错误。我不知道为什么我不能引用派生类并将其传递给一个接受基类引用的方法。请注意,方法foo()和bar()不一定具有相同的语义,因此它们应该具有不同的名称,这些方法不是问题。
public class X { public int _x; }
public class Y : X { public int _y; }
public class A {
public void foo( ref X x ) {
x._x = 1;
}
}
public class B : A {
public void bar( ref Y y ) {
foo( ref y ); // generates compiler error
foo( ref (X)y); // wont work either
y._y = 2;
}
}
我找到的唯一解决方案是:
public class B : A {
public void bar( ref Y y ) {
X x = y;
foo( ref x ); // works
y._y = 2;
}
}
我知道“y”永远不会在bar()中初始化,但由于它声明为ref本身必须在方法之外进行初始化,因此无法解决问题。你能解决这个问题的任何照明都会有所帮助。我确信它只是我对C#缺乏的理解,这可以在C ++中使用强制转换。
答案 0 :(得分:17)
因为无法确保您不会将参考替换为与您首先传递的类型完全不同的类型的实例。
假设:
class Base
{}
class Hamster : Base
{}
class ADentist : Base
{}
void ohWait(ref Base obj)
{
obj = new ADentist();
}
以这种方式打电话:
var foo = new Hamster();
ohWait(ref foo);
会破坏性的。 Eric Lippert解释说它比我更好: Why do ref and out parameters not allow type variation?
答案 1 :(得分:2)
简单示例说明为什么不允许这样做(使用示例中的类X和Y)
void ReplaceX(ref X x)
{
x = new X();
}
void Test()
{
X x = new X();
ReplaceX(ref x); // Fine, our local variable x is now replaced
Y y = new Y();
ReplaceX(ref y); // Error, since it would replace our local
// variable typed as Y with an instance of X
}
答案 2 :(得分:1)
当你提供一个需要ref X
ref Y
而不是ref Y
的函数时,你会说“在这里,我得到了一个X,你可以改变引用,让它指向一个新对象。 “
虽然每个派生的类都可以用来代替它的超类,但是相反的方法并不适用。
该方法可以简单地将您提供的X
指向Y
的{{1}}实例,而不是{{1}}。那不可能奏效。至少不使用C#的类型系统。
答案 3 :(得分:1)
假设foo实际上是这样做的:
x = new X();
然后B.bar()
中的“y”变量将不再引用Y
的实例,这将是一件坏事。
有关详细信息,请参阅Eric Lippert's blog post on the topic。
我同意其他海报 - 你不需要使用ref
。有关该主题的更多信息,请参阅我的article on parameter passing。
答案 4 :(得分:0)
请看Eric Lippert撰写的这篇博文:http://blogs.msdn.com/ericlippert/archive/2009/09/21/why-do-ref-and-out-parameters-not-allow-type-variation.aspx
答案 5 :(得分:0)
我知道这是一个非常古老的问题,但是当我试图找到这个问题的解决方案而且不能,我决定为任何决定访问此问题的人发布快速解决方法未来。环绕C#类型系统的最佳方法是使用模板/泛型类型:
public class A {
public void foo<T>( ref T x ) where T : X {
x._x = 1;
}
}
public class B : A {
public void bar( ref Y y ) {
foo<Y>( ref y ); // works now
y._y = 2;
}
}
现在函数foo扩展到X类型的所有子类,但是你必须确保提供正确的总体类型。