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<T>
    • Converter`2
    • IComparable<T>,
    • IObservable<T>, IObserver<T>
    • IProgress<T>
    • Predicate<T>
  • System.Collections.Generic namespace:
    • IComparer<T>, IEqualityComparer<T>
    • IEnumerable<T>, IEnumerator<T>
    • IReadOnlyCollection<T>, IReadOnlyList<T>
  • System.Linq namespace:
    • IGrouping`2
    • IOrderedQueryable<T>, IQueryable<T>

MSDN has a List of Variant Generic Interface and Delegate Types, but it is inaccurate. For example, it says TElement is covariant for IOrderedEnumerable<TElement>, 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://codingonwheels.com/posts/csharp-covariance-and-contravariance-3-samples/
Author
Dixin
Published at
2009-08-31
License
CC BY-NC-SA 4.0