You can use covariance and contravariance to provide polymorphic extension to delegates, arrays, and generics in C#. Here’s how. The C# programming language provides support for variance in two ways: covariance and contravariance. Covariance and contravariance are supported only for reference types. Since C# 4.0, you can specify in and out parameters on generic types as well. Covariance and contravariance are features added with C# 4.0 that provide polymorphic extension to delegates, arrays and even generics. As MSDN states: Covariance and contravariance are terms that refer to the ability to use a less derived (less specific) or more derived type (more specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. While covariance enables you to use a more derived type, contravariance allows you to take advantage of a more generic type that that was specified. By contrast, invariance enables you to use only the type that was specified originally. In essence, an invariant generic type is neither covariant nor contravariant. In C#, the support for covariance and contravariance has been extended till generic types as well. The C# programming language enables you to annotate generic type parameters using both in and out annotations. This in turn enables you to specify if they should work covariantly or contravariantly. Generics enable you to work with data-create typesafe collections and create classes and methods that can accept a type as a parameter. You can take advantage of generics to eliminate redundant code, enforce type safety, and promote code re-usability and maintainability. It should be noted that variance is applicable only on reference types and not on value type. In essence, a conversion of a value type to a reference type is a boxing conversion and hence it is not an example of variance. Understanding covariance in C# Let’s now understand covariance, contravariance and invariance with code examples. First, we’ll explore covariance with a simple example. Note that while object represents the base of all types in .Net, string represents a specific type. Hence, we may say that string is covariant to object. The following code snippet illustrates how IEnumerable is defined in C#. public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); } public interface IEnumerator<out T> : IEnumerator { bool MoveNext(); T Current { get; } } So, it is evident that IEnumerable is also IEnumerable if Derived type extends the Base type. In other words, if you have two types — Derived and Base — with the former inherited from the latter, the following statement holds good: IEnumerable<Base> obj = new List<Derived>(); Note that obj in the above code snippet, is an instance of type IEnumerable. Now, since Object is the base of all types in .Net, the following statement is also valid. IEnumerable<Object> obj = new List<String>(); Here’s another example of covariance – this time using arrays. Refer to the code snippet given below that assigns a string array to an array instance of type object. object[] objArray = new String[100]; Understanding contravariance in C# Let’s now understand contravariance. Contravariance works the opposite way covariance works. Refer to the code snippet given next that illustrates the IComparer interface. public interface IComparer<in T> { int Compare(T left, T right); } Now suppose you have a comparer that can compare two objects. You can then use the same comparer to compare two strings as well. The reason this works is that the IComparer interface in .Net is contravariant in nature and its generic type parameter is marked with the in annotation. This is an example of contravariance. Another good example of contravariance is the Equals method that is used to compare two instances. If you have an Equals method that can compare two instances of a base type, you can use the same method to check for equality of two instances of a derived type (a type that extends the base type) as well. You can learn more on covariance and contravariance from this article. Related content news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks how-to How to use DispatchProxy for AOP in .NET Core Take advantage of the DispatchProxy class in C# to implement aspect-oriented programming by creating proxies that dynamically intercept method calls. By Joydip Kanjilal Nov 14, 2024 7 mins Microsoft .NET C# Development Libraries and Frameworks news Microsoft’s .NET 9 arrives, with performance, cloud, and AI boosts Cloud-native apps, AI-enabled apps, ASP.NET Core, Aspire, Blazor, MAUI, C#, and F# all get boosts with the latest major rev of the .NET platform. By Paul Krill Nov 12, 2024 4 mins C# Generative AI Microsoft .NET feature Can Wasm replace containers? WebAssembly revolutionized browser apps, and promises to upend the server stack. How will it impact containers and Kubernetes? Six experts weigh in. By Bill Doerrfeld Nov 11, 2024 12 mins Containers Kubernetes Cloud Native Resources Videos