C# Frequently Asked Questions

The C# team posts answers to common questions

Why aren't reference types polymorphic?

Q: Why aren't reference types polymorphic?

A: Consider the following code:

using System;
class Dog {
   public string Name;
}

class Test
{
   public static void Swap(ref object a, ref object b) {
      object temp;
      temp = a;
      a = b;
      b = temp;
   } public static void Main() { Dog d1 = new Dog(); d1.Name = "fido"; Dog d2 = new Dog(); d2.Name = "rex"; Swap(ref d1, ref d2); } }


The compiler will report an error on the call to the Swap() function. Why? Consider if the swap function was like this:

   public static void Swap(ref object a, ref object b) {

a = 5;

b = “Hello“;

   }

If the compiler allowed this code, it would mean assigning a boxed int to a Dog object, which is clearly not type safe.

[Author: Eric Gunnerson]

Published Thursday, April 08, 2004 9:55 PM by CSharpFAQ

Comments

 

Dan Crevier said:

And, the simple solution is generics:

public static void Swap<T>(ref T a, ref T b)
{
T temp a;
a = b;
b = temp;
}
April 8, 2004 10:20 PM
 

Oskar Austegard said:

Ok - I still don't get this. Why does this still compile? a and b are still called by reference, right?
[code]
public static void Swap2(object a, object b)
{
object temp;
temp = a;
a = b;
b = temp;
}
[/code]
and
[code]Swap2(d1, d2);[/code]
April 9, 2004 6:05 AM
 

Dan Crevier said:

Without ref, d1 and d2 aren't modified by the call to Swap2. You'll see that they didn't actually get swapped. In the a=5, b="hello" case, you just change those local variables and d1 and d2 are left pointing to the dog objects.
April 9, 2004 9:44 AM
 

Johan Ericsson said:

Oskar,
Your example compiles but doesn't actually perform a swap.

--
I think the terminology of this stuff is very confusing. There are reference types and there are value types. There is pass by value and pass by reference. Pass by reference is completely orthogonal to a reference type. So, I don't think the title is correct, because you are talking about pass by reference as opposed to reference types in general.

Perhaps something like: "Why is there no implicit base class conversion when a reference is passed by reference to a method?"

Clear as mud?
April 9, 2004 9:44 AM
 

Jon Skeet said:

Agreed, the title is confusing.

For more discussion of parameter passing, see

http://www.pobox.com/~skeet/csharp/parameters.html
April 10, 2004 4:44 AM
 

Dan Crevier's Blog said:

April 12, 2004 12:50 AM
 

Oskar Austegard said:

Jon,

Thanks for a very enlightening article. I was one of the many (surely) who confused reference types and reference parameters. I now understand what Dan and Johan mean - of course they are correct.

Again, thank you.
April 12, 2004 7:00 AM
 

Ernst Kuschke said:

In .NET, objects are passed by value, by default, so without the "ref", there is no reference passed.
April 13, 2004 2:25 AM
 

daniel said:

that is like saying why arent apples oranges
April 14, 2004 1:54 PM
 

Jon Skeet said:

Ernst: No, without the "ref" a reference *is* passed if the parameter is a reference type - but the reference is passed *by* value.
April 18, 2004 10:33 AM
 

Thomas Eyde said:

This is meaningless. Why can't I code a general method, have it compile and let it fail at runtime if that is what I want? My usage of swap will always pass two references of the same type. The code looks correct to me. Why do I always have to type cast or create a strongly typed anything?
April 19, 2004 4:25 AM
 

Jon Skeet said:

Because some of us rather like working in a strongly typed environment.

If you don't, use VB.NET with Option Strict Off.
April 20, 2004 2:15 AM
 

Ray Moody said:

Ok. So why aren't output parameters polymorphic. Of course, instead of passing in argument that derives from the formal parameter, you would have to do the reverse: The formal parameter would have to derive from the argument.

public class C {
public void Copy(StreamReader a, out StreamReader b) {b = a;}
public void WhyNot() {
StreamReader a = new StreamReader(...);
TextReader b;
Copy(a, out b); // Won't compile, but could.
}
}
April 20, 2004 11:42 AM
 

Ben Liu said:

If I modify the swap function as:
Dog tmp = a;
a.Name = b.Name;
b.Name = tmp.Name;
the the swap will not give the correct value;
How is this differ from:
Dog temp= a;
a = b;
b = temp;
April 20, 2004 1:34 PM
 

Jon Skeet said:

Ray: Output parameters possibly could be covariant as described, but I think most people would find that fairly confusing.

Ben: Your swap differs entirely, because in the first version a and tmp are references to the same object - so when you've done a.Name = b.Name, you no longer have a reference to the original a.Name - assigning b.Name = tmp.Name is redundant. That's part of how references work, and nothing to do with reference parameters.
April 21, 2004 2:27 AM
 

Kael Rowan said:

I agree with Jon, plus the fact that the only difference between an out and a ref is an attribute in the IL. VB for example will attempt to treat out parameters as ByRef. Here's a simpler example for those out there who don't realize that StreamReader derives from TextReader:

>WhyNot()
>{
> int i = 1; // int
> object j = 1.2; // boxed double
> Copy(i, out j);
>}
>
>void Copy(int a, out int b)
>{
> // at this point b (object j) is typed as an int but is actually a double. In C# this may be ok since you'll get an error if you attempt to access it before it's assigned to, but other languages might not be so strict.
> b = a; // assigning int to (object j)
>}

-Kael
April 21, 2004 10:18 AM
 

Thomas Eyde said:

Isn't it strange? Whenever someone want to do something in a certain way, because it is effective coding, backed up by tests and you have to be really incompetent to fail, but C# prevents you from doing it then:

1. Someone says "We thought about it but decided not to".

2. "We'd like to change it, but it could break existing code so we won't" (But break-changing framework classes is apparently ok).

3. Someone ask me to use VB.NET.

My desire it to code as efficient as possible. In .Net that means coding in C#. I feel C# could be even more effective with a combination of language changes and improved tool support. VB.NET is not the answer. Nor are generics (which I have to wait for a loooong time, anyway)
April 23, 2004 3:27 AM
 

Thomas Eyde said:

I can create a Swapper class which does what I want. I can't use ref, but I can return an object with the values swapped. This gives the same result, but requires more code for me. Now I have circumvented the compiler controls and got what I wanted. You can reach at least two conclusions from this:

1. If you want to shoot yourself in the foot, go ahead and don't blame us.

2. If the compiler is too restrictive and force a certain coding style on us, then you will see a lot of stupidity as this. Not the code, but the added coding effort.

My point is, unrelated to which camp you are in, the compiler can't really protect us so please, stop trying.

Here's the Swapper:
[Test] public void StaticSwap()
{
int a = 1;
int b = 2;

Swapper p = Swapper.SwapParameters(a, b);

Assert.AreEqual(2, p.A);
Assert.AreEqual(1, p.B);
}

class Swapper
{
public object A, B;

public static Swapper SwapParameters(object a, object b)
{
Swapper p = new Swapper(a, b);
p.Swap();
return p;
}

public Swapper(object a, object b)
{
A = a;
B = b;
}

public void Swap()
{
object t = A;
A = B;
B = t;
}
April 26, 2004 1:46 AM
 

Thomas Eyde said:

Or simply this:

[Test] public void CastedSwap()
{
int a = 1;
int b = 2;

object oa = a, ob = b;

Swap(ref oa, ref ob);

Assert.AreEqual(2, oa);
Assert.AreEqual(1, ob);
}

void Swap(ref object a, ref object b)
{
object t = a;
a = b;
b = t;
}
April 26, 2004 2:04 AM
 

Thomas Eyde said:

The sample, btw, compiles ok:

public static void Swap(ref object a, ref object b)
{ a = 5; b = "Hello"; }

So I don't understand what the fuzz is about. This Swap method clearly has no clue on what it operates on, so where's the type safety? According to that argument, this signature shouldn't even be allowed.
April 26, 2004 2:11 AM
 

Daniel O'Connell said:

You are missing the point entirely, Thomas. That method *is* legal, it is just not legal to pass variables typed as anything but object to it. The type safety is in that you *CANNOT* change the value of a string variable into an int using a ref. You seem to miss that point. A variable typed object contains a reference to an object, nothing more. When you change its value it is still a variable of type object, but *is is not the same object*. When the variable is type string, then the reference *has* to point to a string or your code is broken. By allowing you to pass a string into a ref object parameter would mean allowing you to point a string variable at an integer, which is a very bad thing, IMHO. The best case scenario will probably be am unexpected nonfatal exception(NullReferenceException, MissingMethodExceptoin, whathave you), the worst(and more likely) would be a fatal ExectionEngineException. If you don't understand that, then you need to do some research and understand how ref works. I doubt the runtime will even allow you to do this, but I could be wrong.

The example does subvert type safety and your examples don't illustrate the same issue in any way.
April 26, 2004 1:31 PM
 

Thomas Eyde said:

I am not missing the point, I just view the matter differently.

What is the difference between passing a variable of object pointing to an int, than passing a variable of int pointing to the same int? Semanticly nothing.

The code works when I introduce helper variables of the expected type. The code itself is no nearer to know anything about what they contain, but happily accepts it.

If this is legal:

int a=1, b=2;
object oa = a, ob = b;
Swap(ref oa, ref ob);

Then this should also be legal:

int a=1, b=2;
Swap(ref a, ref b);

Both examples do same thing. Yes, I could pass incompatible references to Swap() like this:

int a = 1; string b = "b";
Swap(ref a, ref b);

I expect that situation to be treated like this one:
ArrayList list = new ArrayList();
list.Add(1);
list.Add("b");
int a = (int)list[1];
string b = (string)list[0];

Which also proves that explicit typing will save you from very little. But it enables intellisense, which is a very nice thing.
April 27, 2004 6:27 AM
 

Daniel O'Connell said:

Actually, your still missing the point. When you pass a ref, you aren't passing the variable. You are passing the address of the variable. There is no cast, as far as the runtime is concerned the variable is of type object and can take anything. What you are doing here would probably work with a copyout approach but not ref.

The examples do two different things, In the first one, you are passing a pointer to the variables oa and ob, in the second you are passing a pointer to a & b. The contents aren't passed and don't matter, its the type of the variable pointed to. Assigning a string to an object variable is legal, assigning it to an int variable is not. You seem to be missing the actual semantics of ref more than anything else.

I also don't see how this adds intellisense though.
April 27, 2004 1:15 PM
 

Thomas Eyde said:

Explicit typing adds intellisense. If the IDE doesn't know the type, how can it list the proper members?

I know that assigning a string to an int is illegal. My point is that the program doesn't know until runtime. If a ref parameter is of type object, then how can the compiler decide what's legal or not? You and I as human beings can tell when we know the whole context.

I think this ref restriction is meaningless. We can achieve what we want with more code. Is that the goal of C#? Code bloat?

The ref enables me to change the pointer. I can change the pointer without the ref, too, but the changes will not apply outside of the method.

Using your arguments, ref object should not be allowed. With my arguments, ref object should allow anything because all sub types derives from object. Instead the compiler treats this scenario differently, and I have code that lies to me. It says it accepts everything, but the compiler doesn't.

I don't believe there are no technical reasons to not enable ref to be polymorphic. The reason it isn't is a political decition from the C# designers that this is not good for us.

I am starting to think that ref and out should not be part of the language. we can obtain the same results with parameter objects.
April 28, 2004 12:46 AM
 

Paul Asarak said:

Referring to the original code, instead of declaring d1 and d2 as Dog type, you could declare them to be object type. The Swap method would have happily swapped the two objects.

If Swap had done something strange like assigning int to one variable and string to another, then it's not doing the intended work of swapping references.

If the argument is you can't assign variable of one type for another unrelated type, then I agree.
April 28, 2004 10:37 AM
 

Daniel O'Connell said:

Ok, heres the deal. It is possible, in IL, to assign an int reference to a string. However it results in unverifiable code. As a result only administrators and other people with privildges to use unverifiable code would be able to use it. Part of the point of C# is to make it easier to produce verifiable code. Here is the output of an IL assembly that does what you want
[IL]: Error: [C:\ref.exe : reftest.Class1::MyRefMethod][offset 0x00000010][found
Int32][expected ref 'System.String'] Unexpected type on the stack.
1 Error Verifying ref.exe

Now, having pointed that out, even if you manage to get around the verifiability, the resultant code will eventually result in a MethodMissingException, somewhere further down the line(the first time a method is called on it, hopefully). This construct is all around bad and would be a good source of bugs and security issues.

Frankly, before you argue technical reasoning here, I think you have a great deal to learn about the semantics of the issues involved. Your examples and arguments here havn't shown you actually know what you are talking about. Your grasp of reference parameters and the general .NET runtime is probably the cause of your problem understanding this.

Lets start with how references work. A reference literally refers to a piece of data, it effectivly indexes an object's current native pointer(note this is a bit different than managed pointers). These references are stored in variables, not the objects themselves. Now, variables are constraining entities. They declare that this reference is of atleast type T or null. Because of this, we get some nice things like type safety, intellisense, etc. However there are some caveats to it, one is the assumption that a string variable will always point to a string and the language and runtime does its best to ensure that. Looser typed languges, like C++, do some small amounts of checking but still allow you to place data into a variable that doesn't match its type. This can work when you can consider phyiscal layout, but in the .NET scheme, physical layout is irrelevent. Methods are constrained to the type they reference and aren't just firing off to whatever value lays in the vtable at the location the method you actually referenced should be.

Now, let us consider how ref parameters work. A reference parameter is, in effect, a pointer passed to a method that points to the *reference* which is intended to be used. One of the key differences here in being able to change the pointer directly and being able to do it with extra objects is a different matter entirely, it involves casts and type checks to assign a value to a variable of type string, ensuring its type string; A ref object wouldn't require those checks, since it is of type object. The point isn't so much that the method can't take everything, it can, it just can't be contained in a variable typed other than an object.

Having said that, if you don't like it, too bad. It is the right choice and I'm tired of arguing the point. If you really want a system where you can screw everything up and hope that somewhere down the line someone will realize it, go for it, C++ is wide open. This behavior you are championing would more or less break every principal of C# and make ref and out parameters more than useless, it would make them dangerous.
April 28, 2004 10:56 AM
 

Thomas Eyde said:

>The point isn't so much that the method can't take everything, it can, it just can't be contained in a variable typed other than an object.

That's my point. I think the language should support subtypes passed in. If I can write a workaround, then the compiler could in theory do the same, right?
April 28, 2004 12:15 PM
 

Daniel O'Connell said:

It could, but I think it would be much the worse for wear if it did so. You were worried about code lying to you earlier, but your suggestion at making the compiler work around is *really* lying. The code you write would have no similarity to the code that is generated, which is unexpected to say the least.

One system which has been discussed on the newsgroups offers a potentially better solution then out\ref. Basically it is a copy in, copy out variant on ref(similar to a construct in ADA and another language I can't recall) which generates a copy of the value you are passing in and then copies the result back to the target expression. It has a few semantic boosts with a few potential pitfalls. I do think, however, that it would semantically be capable of handling polymorphic references, however the trial implementation I've put together will still fail in that situation. While inout parameters would still have some issues with passing in a string variable to an object parameter, the exception generated should be a InvalidCastException, not a MissingMethodException somewhere down the line when you try to access a method that doesn't exist on object or on an interface the two happen to share. I think the semantics of this system are alot closer to the semantics your posts have alluded to. That, of course, isn't to say that it would be a good idea to allow behavior like that, and I'd certainly say anyone who wrote code using it was asking for trouble, but it would be possible, unlike the current ref behavior.

However, I certainly can't speak about it being added to the language standard, a few of us just discussed it. I fought it pretty heavily, but after creating the trial implementation I've been forced to agree its not bad...and considerably clearer, semantically anyway, than ref and the rules around it. It would make a good addition, IMHO, although its got almost as many rules and gotchas as ref does.
April 28, 2004 2:33 PM
 

Thomas Eyde said:

"It could, but I think it would be much the worse for wear if it did so. You were worried about code lying to you earlier, but your suggestion at making the compiler work around is *really* lying. The code you write would have no similarity to the code that is generated, which is unexpected to say the least. "

I thought the purpose of a high level language was to abstract away the actual executing code? If we dive deep enought, will not all compiler generated code be very unsimilar to the source code?
May 14, 2004 12:38 AM
 

Daniel O'Connell said:

Ok, heres the deal. It is possible, in IL, to assign an int reference to a string. However it results in unverifiable code. As a result only administrators and other people with privildges to use unverifiable code would be able to use it. Part of the point of C# is to make it easier to produce verifiable code
June 9, 2004 9:27 PM
 

dianying xia zai said:

[href=http://www.dmoz.net.cn/ wangzhidaquang]
[href=http://www.86dmoz.com/ jingpingwangzhi]
[href=http://movie.kamun.com/ mianfeidianying]
[href=http://www.kamun.com/ dianyingxiazai]
[href=http://music.kamun.com/ MP3 free download]
[href=http://www.pc530.net/ diannaoaihaozhe]
[href=http://www.5icc.com/ duangxingcaixingxiazha]
[href=http://www.dianyingxiazai.com/ dianyingxiazai]
[href=http://www.yinyuexiazai.com/ yinyuexiazai]
August 1, 2004 11:54 PM
 

Eric Joe said:

Ping Back来自:blog.csdn.net
September 16, 2004 2:02 AM
 

RebelGeekz said:

December 28, 2004 4:54 AM
 

c ref object parameters said:

May 21, 2008 11:09 AM
Anonymous comments are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker