开发者

Why wont reference of derived class work for a method requiring a reference of base class?

I get a compiler error below. I dont know why I cant take a reference of a derived class and pass it to a method which takes a reference of the base class. Note that methods foo() and b开发者_StackOverflowar() doesnt necessarily have the same semantics so they should have different names, these methods are not the issue.

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;  
  }  
}

The only solution I found was:

public class B : A {  
  public void bar( ref Y y ) {  
    X x = y;
    foo( ref x ); // works
    y._y = 2;  
  }  
}

I know "y" is never initialized in bar() but since its declared as ref itself must be initialized outside the method so that cant be the problem. Any illumination you can shed on this matter would be helpful. I'm sure its just my understanding of C# thats lacking, this would work in C++ with a cast.


Because there's no way to ensure you won't replace the reference with an instance of a type entirely different from what you've passed in the first place.

Given:

class Base
{}

class Hamster : Base
{}

class ADentist : Base
{}


void ohWait(ref Base obj)
{
    obj = new ADentist();
}

When calling it this way:

var foo = new Hamster();
ohWait(ref foo);

would break atrociously. Eric Lippert explains it way better than me in: Why do ref and out parameters not allow type variation?


I know that this is a very old question, but seeing as I was trying to find a solution to this problem and couldn't, I'm deciding to post a quick workaround for any people who decide to visit this question in the future. The best way to circumnavigate C#'s type system is to use templating/generic types to your advantage:

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;
  }  
}

Now the function foo extends to all of the subclasses of type X, but you have to make sure you supply the correct overarching type.


Simple example to illustrate why this is not allowed (using classes X and Y from your example)

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
}


When you give a function that requires ref X a ref Y instead you're saying "Here, I got an X for you, you can change the reference to let it point to a new object."

While every derived class can be used in place of its superclass, the same doesn't hold true the other way around.

The method can simply point your ref Y you gave it to an instance of X which isn't a Y. That can't possibly work. At least not with C#'s type system.


Suppose that foo actually did this:

x = new X();

Then your "y" variable in B.bar() would no longer refer to an instance of Y, which would be a Bad Thing.

For a lot more details on this, see Eric Lippert's blog post on the topic.

I concur with other posters though - you don't need to use ref. See my article on parameter passing for more information on that topic.


Have look at this blog post by Eric Lippert: http://blogs.msdn.com/ericlippert/archive/2009/09/21/why-do-ref-and-out-parameters-not-allow-type-variation.aspx

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜