I just learned that having a generic argument as the type of an out
parameter forces that generic type to be invariant. This is surprising to me. I thought out
parameters are treated the same as return types (i.e. if the generic parameter is covariant, then it can be used in as an out
out parameter), since they are both "outputs" of a method.
After a bit of investigation, I realised that you can't do this:
public class Program {
public static void Main() {
// cannot convert from 'out object' to 'out string'
F(out object s); // passing an out object
}
public static void F(out string o) {
o = null;
}
}
This explains why out
parameters must be invariant. However, I still don't understand why you can't do this. As is commonly known, out
parameters are just another way of returning a value. F
could be rewritten with a return value, and it will work:
// This is the semantically equivalent version of the above, just without "out"
public class Program {
public static void Main() {
object s = F();
}
public static string F() {
return null;
}
}
So why doesn't the first code snippet compile? Does using out
allow F
to do something that can't be done with return values, that will break type-safety if an out object s
were passed to it?
I found this question, which is about converting the other way - from a derived class to a base class, which clearly isn't possible. You can't assign the return value of a method that returns a object
to a variable of type string
, can you?
What I'm asking is, since you can assign the return value of a method that returns string
to a variable of type object
, why can't you do the same with out
parameters? That is, why can't you pass an out object
to a out string
parameter?
I also read the docs and the spec, but they never mentioned anything about the fact that you have to pass the exact same type into a out
parameter, let alone explain why you have to do it.
With out
parameters the argument is passed by reference just like ref
, the difference is that the value must be assigned by the end of the method and the reference does not need to be initialized before calling. But it can be initialized before and the method can read the initial value.
From the docs: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier
The out keyword causes arguments to be passed by reference
It is like the ref keyword, except that ref requires that the variable be initialized before it is passed
As the method can read the variable, the reference must be of type string
to work. The reading blocks covariance and the output blocks contravariance, thus the argument must be invariant.
As is commonly known, out parameters are just another way of returning a value
Not true: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out
As a parameter modifier, which lets you pass an argument to a method by reference rather than by value.
That means that you are passing on a reference to a specific object.
I think your answer though is here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref
Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller.
So when you are passing an object to the function you are effectively doing an assignment of type
derived <- base
and when you are assigning from inside the function
base <- derived
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With