Why doesn't 'ref' and 'out' support polymorphism?
=============
UPDATE: I used this answer as the basis for this blog entry:
Why do ref and out parameters not allow type variation?
See the blog page for more commentary on this issue. Thanks for the great question.
=============
Let's suppose you have classes Animal
, Mammal
, Reptile
, Giraffe
, Turtle
and Tiger
, with the obvious subclassing relationships.
Now suppose you have a method void M(ref Mammal m)
. M
can both read and write m
.
Can you pass a variable of type
Animal
toM
?
No. That variable could contain a Turtle
, but M
will assume that it contains only Mammals. A Turtle
is not a Mammal
.
Conclusion 1: ref
parameters cannot be made "bigger". (There are more animals than mammals, so the variable is getting "bigger" because it can contain more things.)
Can you pass a variable of type
Giraffe
toM
?
No. M
can write to m
, and M
might want to write a Tiger
into m
. Now you've put a Tiger
into a variable which is actually of type Giraffe
.
Conclusion 2: ref
parameters cannot be made "smaller".
Now consider N(out Mammal n)
.
Can you pass a variable of type
Giraffe
toN
?
No. N
can write to n
, and N
might want to write a Tiger
.
Conclusion 3: out
parameters cannot be made "smaller".
Can you pass a variable of type
Animal
toN
?
Hmm.
Well, why not? N
cannot read from n
, it can only write to it, right? You write a Tiger
to a variable of type Animal
and you're all set, right?
Wrong. The rule is not "N
can only write to n
".
The rules are, briefly:
1) N
has to write to n
before N
returns normally. (If N
throws, all bets are off.)
2) N
has to write something to n
before it reads something from n
.
That permits this sequence of events:
- Declare a field
x
of typeAnimal
. - Pass
x
as anout
parameter toN
. N
writes aTiger
inton
, which is an alias forx
.- On another thread, someone writes a
Turtle
intox
. N
attempts to read the contents ofn
, and discovers aTurtle
in what it thinks is a variable of typeMammal
.
Clearly we want to make that illegal.
Conclusion 4: out
parameters cannot be made "larger".
Final conclusion: Neither ref
nor out
parameters may vary their types. To do otherwise is to break verifiable type safety.
If these issues in basic type theory interest you, consider reading my series on how covariance and contravariance work in C# 4.0.
Why doesn't polymorphism work without pointers/references?
In C++, an object always has a fixed type and size known at compile-time and (if it can and does have its address taken) always exists at a fixed address for the duration of its lifetime. These are features inherited from C which help make both languages suitable for low-level systems programming. (All of this is subject to the as-if, rule, though: a conforming compiler is free to do whatever it pleases with code as long as it can be proven to have no detectable effect on any behavior of a conforming program that is guaranteed by the standard.)
A virtual
function in C++ is defined (more or less, no need for extreme language lawyering) as executing based on the run-time type of an object; when called directly on an object this will always be the compile-time type of the object, so there is no polymorphism when a virtual
function is called this way.
Note that this didn't necessarily have to be the case: object types with virtual
functions are usually implemented in C++ with a per-object pointer to a table of virtual
functions which is unique to each type. If so inclined, a compiler for some hypothetical variant of C++ could implement assignment on objects (such as Base b; b = Derived()
) as copying both the contents of the object and the virtual
table pointer along with it, which would easily work if both Base
and Derived
were the same size. In the case that the two were not the same size, the compiler could even insert code that pauses the program for an arbitrary amount of time in order to rearrange memory in the program and update all possible references to that memory in a way that could be proven to have no detectable effect on the semantics of the program, terminating the program if no such rearrangement could be found: this would be very inefficient, though, and could not be guaranteed to ever halt, obviously not desirable features for an assignment operator to have.
So in lieu of the above, polymorphism in C++ is accomplished by allowing references and pointers to objects to reference and point to objects of their declared compile-time types and any subtypes thereof. When a virtual
function is called through a reference or pointer, and the compiler cannot prove that the object referenced or pointed to is of a run-time type with a specific known implementation of that virtual
function, the compiler inserts code which looks up the correct virtual
function to call a run-time. It did not have to be this way, either: references and pointers could have been defined as being non-polymorphic (disallowing them to reference or point to subtypes of their declared types) and forcing the programmer to come up with alternative ways of implementing polymorphism. The latter is clearly possible since it's done all the time in C, but at that point there's not much reason to have a new language at all.
In sum, the semantics of C++ are designed in such a way to allow the high-level abstraction and encapsulation of object-oriented polymorphism while still retaining features (like low-level access and explicit management of memory) which allow it to be suitable for low-level development. You could easily design a language that had some other semantics, but it would not be C++ and would have different benefits and drawbacks.
Why ref parameters can not be ignored like out parameters?
The reason is simple: because you're not allowed to pass an uninitialized variable into a ref
parameter. This has always been the case, and the new syntactical sugar in C#7 doesn't change that.
Observe:
int i;
MyOutParameterMethod(out i); // allowed
int j;
MyRefParameterMethod(ref j); // compile error
The new feature in C#7 allows you to create a variable in the process of calling a method with an out
parameter. It doesn't change the rules about uninitialized variables. The purpose of a ref
parameter is to allow passing an already-initialized value into a method and (optionally) allow the original variable to be changed. The compiler semantics inside the method body treat ref
parameters as initialized variables and out
parameters as uninitialized variables. And it remains that way in C#7.
Why polymorphism doesn't work on operators via object types?
First of all let's look at the Object class itself:
As you can see the Equals method has virtual
key on it, meaning that in any class that inherits from object, if you override
that method, then even if your variable object o2 = a2;
is typeof object pointing to new instance of A
class -> this method will be invoked by the runtime:
public override bool Equals(object other)
On the other hand the operator ==
is not virtual
, it cannot be overriden, so when referring to object
variable it will always resolve to System.Object.ReferenceEquals
Why are the switch-case part and runtime polymorphism is not working?
you never populated your farmers1 list, it is always empty therefore isFruitAvailable is always false. Also, your while loop never terminates, it just keeps spinning ("break" statements that you have are inside switch and have nothing to do with "while")
for(Farmer fruit : farmers1) {
if(fruit.getName().equals(tempName)) {
farmers1.add(fruit); //the only place where you add something, but it never gets executed
break;
}
}
C# ref Polymorphism workaround
Generics perhaps?
private void customShow<T>(ref T someForm) where T : Form, new()
{
if (someForm == null || someForm.IsDisposed) someForm = new T();
someForm.StartPosition = FormStartPosition.CenterScreen;
someForm.MdiParent = this;
someForm.Show();
someForm.WindowState = FormWindowState.Maximized;
}
And then I wanted to do this:
private void mnuKategori_Click(object sender, EventArgs e)
{
customShow(ref frmKategori);
frmKategori.isCRUD = true;
}
Problem with string as reference parameter when method takes Object C#
This is a duplicate of
Why doesn't 'ref' and 'out' support polymorphism?
See that answer for why it doesn't work.
To make it work, you could make a generic method:
public static void Swap<T>(ref T a, ref T b)
{
T t = b;
b = a;
a = t;
}
And now all the types check out.
Why doesn't returning by ref work for elements of collections?
The answer is in that same link you posted:
You can only return refs that are “safe to return”: Ones that were
passed to you, and ones that point into fields in objects.
Your example satisfies neither. You are creating the list inside the function (so the object will go out of scope and its pointer will be invalid), and it doesn't point to a field of an object.
Related Topics
How to Get All Classes Within a Namespace
Hide Tabcontrol Buttons to Manage Stacked Panel Controls
Parallel.Foreach VS Task.Run and Task.Whenall
Multipart Forms from C# Client
When Should I Use "Using" Blocks in C#
How to Parse a Month Name (String) to an Integer for Comparison in C#
Why Doesn't C# Support the Return of References
How to Transform String to Utf-8 in C#
How to Customize the System Menu of a Windows Form
Why Does Boolean.Tostring Output "True" and Not "True"
Stopwatch VS. Using System.Datetime.Now for Timing Events
Ef Including Other Entities (Generic Repository Pattern)
Embedding an External Executable Inside a C# Program
How to Send Ctrl+C to a Process in C#