Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I convert from 'out BaseClass' to 'out DerivedClass'?

Tags:

c#

out

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.

like image 822
Sweeper Avatar asked Sep 12 '20 08:09

Sweeper


Video Answer


2 Answers

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.

enter image description here

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.

like image 98
Jason Avatar answered Oct 13 '22 20:10

Jason


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

like image 1
Athanasios Kataras Avatar answered Oct 13 '22 20:10

Athanasios Kataras