Skip to content

Return type co-variance in C#

20110325 (2011 March 25 Friday)

Return type covariance is a safe way to make a statically typed language seem more dynamic. It is a system of allowing the programmer to tighten up on the return type of an overloaded method. Return type co-variance is safe, and it can be implemented by slackening a rule in the static analyser.

E.g.


interface A {
    EndPoint RemoteEndPoint();
}

class B : A {
    public IPEndPoint RemoteEndPoint();
}

.net does not have return type covariance. This is a shame as it would give a more dynamic feel to the language ( as var and generics do ). It would also eliminate one of the last unnecessary uses of casts or as operator. Return type covariance is supported by C++ and Eiffel including Eiffel.net (though not across languages).

(Casts should be consigned to history as there use is overloaded making them confusing. The as operator is good when you need dynamic typing such as de-serialisation. But that is it, it should not be used elsewhere.)

Therefore:

  • Whenever designing a language allow return type covariance.
  • Whenever using a language that does not support it, try to find a way around it.

I have found a way around it in C#. It requires extra boiler plate code in the implementation (much like accessors).

It can be added retrospectively as the boiler plate code uses this methods, so goes in a different class.

Simple example shown below.


using System;

namespace return_type_covariance
{
    public interface A1{} 
    public class A2 : A1{}
    public class A3 : A1{}
    
    public interface B1 
    {
        A1 theA();
    }
    
    public class B2 : B1
    {
        public A1 theA()
        {
            return new A2();
        }
    }
    
    public static class B2_ReturnTypeCovariance
    {
        public static A2 theA_safe(this B2 b)
        {
            return b.theA() as A2;    
        }
    }
    
    public class B3 : B1
    {
        public A1 theA()
        {
            return new A3();    
        }
    }
    
    public static class B3_ReturnTypeCovariance
    {
        public static A3 theA_safe(this B3 b)
        {
            return b.theA() as A3;    
        }
    }
    
    public class C2
    {
        public void doSomething(A2 a){}    
    }
    
    class MainClass
    {
        public static void Main (string[] args)
        {
            var c2 = new C2();
            var b2 = new B2();
            var a2=b2.theA_safe();
            
            c2.doSomething(a2);
        }
    }
}

More practical example


using System;
using System.Net;
using System.Net.Sockets;

namespace My.Util
{
    public static class SocketReturnTypeCovariants
    {
        public static IPEndPoint RemoteEndPoint_safe(this Socket soc)
        {
            return soc.RemoteEndPoint as IPEndPoint;
        }
    }
}

Example usage:


using System;
using System.Net;
using System.Net.Sockets;
using My.Util;
namespace returntypecovariancetest
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			var listener = new Socket(
				AddressFamily.InterNetwork,
			    SocketType.Stream,
			    ProtocolType.Tcp );
			                             ;
			listener.Bind(new IPEndPoint(IPAddress.Any,60000));
			listener.Listen(1);
			
			var soc = listener.Accept();
			
			IPEndPoint remoteEndPoint = soc.RemoteEndPoint_safe(); // no as here
			
			Console.WriteLine ("end point is:" + remoteEndPoint.ToString() +"\n");
		}
	}
}
Advertisements
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: