Skip to content

Generic covariant C# 4.0type safety

Mark a generic type parameter with `out` so a more derived type argument can be used.

Object-oriented programming allows types to be substituted in some scenarios. For example, if a method returns us an Account it is fine for that method to return us a SavingsAccount instead. Covariance extends this idea to types built from other types: if a SavingsAccount is an Account, then a sequence of SavingsAccount can be used as a sequence of Account.

C# 1.0 already had covariance for arrays (Account[] all = new SavingsAccount[10];, which can fail at runtime when writing), and C# 2.0 allowed a method returning a more derived type to be used for a delegate. Generic covariance brings covariance to generic interfaces and delegates.

Let us consider IList<T>. We can never assign an IList<SavingsAccount> to an IList<Account> because the interface both accepts arguments of T (Add) and returns T (the indexer). If it were allowed, someone could Add a plain Account to a list that must only hold savings accounts.

But what about an interface that only returns instances of T like an enumerable? With generic covariance we can do this!

Code ​

csharp
IEnumerable<Account> accounts = getAccounts<SavingsAccount>();
foreach(var account in accounts)
    PrintStatement(account);
csharp
// IEnumerable<Account> accounts = getAccounts<SavingsAccount>(); // error CS0266
List<Account> accounts = new List<Account>();
foreach(SavingsAccount savings in getAccounts<SavingsAccount>())
    accounts.Add(savings);
foreach(Account account in accounts)
    PrintStatement(account);

This was achieved in .NET 4 by Microsoft changing IEnumerable<T> to IEnumerable<out T>.

Notes ​

  • Variance only applies to generic interfaces and delegates, not classes, so List<SavingsAccount> still cannot be assigned to List<Account>
  • Variance only works for reference types: IEnumerable<int> cannot be used as IEnumerable<object>
  • The reverse of this - accepting a more specific argument rather than having a more specific one returned to you - is Generic contravariance C# 4.0
  • Covariant returns C# 9.0 later allowed overriding methods to return a more derived type

More information ​

Powered by Intracia