【问题标题】:Why wont reference of derived class work for a method requiring a reference of base class?为什么派生类的引用不能用于需要基类引用的方法?
【发布时间】:2009-10-29 10:45:08
【问题描述】:

我在下面收到编译器错误。我不知道为什么我不能引用派生类并将其传递给一个引用基类的方法。请注意,方法 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++ 中使用演员表。

【问题讨论】:

  • 你为什么首先作为 ref 传递?从您的示例来看,您似乎不需要使用 ref.只需按值传递引用即可。
  • 我认为这只是一个非常精简的示例,它展示了问题而不是使用它的完整代码。
  • 很像 Johannes,不像是生产代码。 :)

标签: c#


【解决方案1】:

因为无法确保您不会将引用替换为与您最初传递的类型完全不同的实例。

给定:

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?

【讨论】:

  • 对,当然可以。虽然为什么有人会以这种方式设计一种方法,但我无法理解,如果他们这样做了,他们只能怪罪自己。有时 C# 太像代码警察了。谢谢大家的回答。
【解决方案2】:

我知道这是一个非常古老的问题,但鉴于我试图找到解决此问题的方法但无法解决,我决定为任何决定访问此问题的人发布一个快速解决方法未来。绕过 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 类型的所有子类,但您必须确保提供正确的总体类型。

【讨论】:

  • 你也可以在 T 的 where 子句中添加 ", new()" ,这样它也可以在 foo 函数中实例化。
【解决方案3】:

说明为什么不允许这样做的简单示例(使用示例中的 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
}

【讨论】:

    【解决方案4】:

    当您提供需要 ref Xref Y 的函数时,您是在说“这里,我为您准备了一个 X,您可以更改引用以使其指向一个新对象。”

    虽然每个派生的类都可以用来代替它的超类,但反过来就不一样了。

    该方法可以简单地将您提供的ref Y 指向X 的实例,该实例不是Y。那是不可能的。至少 C# 的类型系统不会。

    【讨论】:

      【解决方案5】:

      假设 foo 实际上是这样做的:

      x = new X();
      

      那么B.bar() 中的“y”变量将不再引用Y 的实例,这将是一件坏事。

      有关更多详细信息,请参阅Eric Lippert's blog post on the topic

      我同意其他海报 - 你不需要使用ref。有关该主题的更多信息,请参阅我的article on parameter passing

      【讨论】:

        【解决方案6】:
        猜你喜欢
        • 2011-05-11
        • 1970-01-01
        • 2022-01-21
        • 1970-01-01
        • 1970-01-01
        • 2017-12-18
        • 2011-04-23
        • 2016-05-28
        • 1970-01-01
        相关资源
        最近更新 更多