C# ‘out’ Keyword – Understanding Its Dual Context Usage

ckeywordslanguage-designterminology

In C#, the out keyword can be used in two different ways.

  1. As a parameter modifier in which an argument is passed by reference

    class OutExample
    {
        static void Method(out int i)
        {
            i = 44;
        }
        static void Main()
        {
            int value;
            Method(out value);
            // value is now 44
        }
    }
    
  2. As a type parameter modifier to specify covariance.

    // Covariant interface. 
    interface ICovariant<out R> { }
    
    // Extending covariant interface. 
    interface IExtCovariant<out R> : ICovariant<R> { }
    
    // Implementing covariant interface. 
    class Sample<R> : ICovariant<R> { }
    
    class Program
    {
        static void Test()
        {
            ICovariant<Object> iobj = new Sample<Object>();
            ICovariant<String> istr = new Sample<String>();
    
            // You can assign istr to iobj because 
            // the ICovariant interface is covariant.
            iobj = istr;
        }
    }
    

My question is: why?

To a beginner, the connection between the two doesn't seem intuitive. The use with generics doesn't seem to have anything to do with passing by reference.

I first learned what out was in relation to passing arguments by reference, and this hindered my understanding of the use of defining covariance with generics.

Is there a connection between these uses that I'm missing?

Best Answer

There is a connection, however it is a little loose. In C# the keywords ´in´ and ´out´ as their name suggest stand for input and output. This is very clear in the case of output parameters, but less clean what it have to do with template parameters.

Lets take a look at the Liskov substitution principle:

...

Liskov's principle imposes some standard requirements on signatures which have been adopted in newer object-oriented programming languages (usually at the level of classes rather than types; see nominal vs. structural subtyping for the distinction):

  • Contravariance of method arguments in the subtype.
  • Covariance of return types in the subtype.

...

See how contravariance is associated with input and covariance is associated with output? In C# if you flag a template variable with out to make it covariant, but please note you can only do this if the mentioned type parameter only appears as output (function return type). So the following is invalid:

interface I<out T>
{
  void func(T t); //Invalid variance: The type parameter 'T' must be
                  //contravariantly valid on 'I<T>.func(T)'.
                  //'T' is covariant.

}

Similary if you flag a type parameter with in, that means you can only use it as input (function parameter). So the following is invalid:

interface I<in T>
{
  T func(); //Invalid variance: The type parameter 'T' must
            //be covariantly valid on 'I<T>.func()'. 
            //'T' is contravariant.

}

So to summarize, the connection with the out keyword is that with function parameters it means that it is an output parameter, and for type parameters it means that the type is only used in output context.

System.Func is also a good example what rwong mentioned in his comment. In System.Func all input parameters ar flaged with in, and the output parameter is flaged with out. The reason is exactly what I described.

Related Topic