294 words
1 minute
Understanding C# Covariance And Contravariance (3) Variances in .NET

Understanding C# Covariance And Conreavariance:

Not many generic types in .NET have variant type parameter(s). LINQ can be uses to query these generic types from .NET libraries.

The following method queries a specified directory, and retrieve all .NET assemblies:

public static partial class ReflectionHelper
{
public static IEnumerable<Assembly> GetAssemblies(string directory)
{
return Directory.EnumerateFiles(directory, "*.dll")
.Select(file =>
{
try
{
return Assembly.LoadFrom(file);
}
catch (BadImageFormatException)
{
return null;
}
})
.Where(assembly => assembly != null);
}
}

The following method queries one specified assembly, and filter generic types with any variant type parameter:

public static partial class ReflectionHelper
{
public static IEnumerable<Type> GetTypesWithVariance(Assembly assembly)
{
try
{
return assembly.ExportedTypes.Where(type =>
type.IsGenericTypeDefinition && type.GetGenericArguments().Any(argument =>
(argument.GenericParameterAttributes & GenericParameterAttributes.Covariant)
== GenericParameterAttributes.Covariant
||
(argument.GenericParameterAttributes & GenericParameterAttributes.Contravariant)
== GenericParameterAttributes.Contravariant));
}
catch (TypeLoadException)
{
return Enumerable.Empty<Type>();
}
}
}

The last method queries the assemblies in the same directory of mscorlib.dll, and retrieves the wanted types, and orders them by name:

public static partial class ReflectionHelper
{
public static IEnumerable<Type> GetTypesWithVariance()
{
string mscorlibPath = typeof(object).Assembly.GetName().CodeBase;
string directory = Path.GetDirectoryName(new Uri(mscorlibPath).AbsolutePath);
return GetAssemblies(directory)
.SelectMany(GetTypesWithVariance)
.OrderBy(type => type.Name);
}
}

Here is the result of executing the last method:

  • System namespace:
    • Action`1 to Action`16, Func`1 to Func`17
    • Comparison
    • Converter`2
    • IComparable,
    • IObservable, IObserver
    • IProgress
    • Predicate
  • System.Collections.Generic namespace:
    • IComparer, IEqualityComparer
    • IEnumerable, IEnumerator
    • IReadOnlyCollection, IReadOnlyList
  • System.Linq namespace:
    • IGrouping`2
    • IOrderedQueryable, IQueryable

MSDN has a List of Variant Generic Interface and Delegate Types, but it is inaccurate. For example, it says TElement is covariant for IOrderedEnumerable, but actually not:

namespace System.Linq
{
public interface IOrderedEnumerable<TElement> : IEnumerable<TElement>, IEnumerable
{
IOrderedEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);
}
}
Understanding C# Covariance And Contravariance (3) Variances in .NET
https://dixin.github.io/posts/understanding-csharp-covariance-and-contravariance-3-samples/
Author
Dixin
Published at
2009-08-31
License
CC BY-NC-SA 4.0