Date due classi A e B, denotiamo con B <: A il fatto che B è sottoclasse di A.
Una classe generica, diciamo C<T>, si dice covariante rispetto a T se, date due classi A e B tali che B <: A, risulta anche che C<B> <: C<A>.
In modo duale, una classe generica, diciamo C<T>, si dice controvariante rispetto a T se, date due classi A e B tali che B <: A, risulta anche che C<A> <: C<B>.
Gli attuali tipi generici C# non contemplano i concetti di covarianza e controvarianza dei type parameter e questa è una limitazione in diversi casi. Consideriamo un esempio:
using
System;
using System.Collections.Generic;
using System.Text;
class
Control
{ }
class
Button : Control
{ }
class
Program
{
static void RenderControls(IEnumerable<Control> controls)
{
foreach (Control control in controls)
// ...
;
}
static IEnumerable<Button> GetButtons()
{
yield return new Button();
yield return new Button();
}
static void Main(string[] args)
{
RenderControls(GetButtons()); // Argument '1': cannot convert from
// 'System.Collections.Generic.IEnumerable<Button>' to
// 'System.Collections.Generic.IEnumerable<Control>'
}
}
Nell'esempio è chiaro che un metodo (RenderControls) in grado di enumerare una sequenza di Control è anche in grado di enumerare una sequenza di Button.
L'argomento è noto da tempo e pone diversi problemi teorici a cui sono state date nel tempo diverse soluzioni. Le diverse proposte tentano chiaramente di introdurre covarianza e controvarianza dei type parameter cercando di mantenere "sound" il type system.
Per chi fosse interessato all'argomento consiglio un ottimo articolo di un esperto italiano (!) dell'argomento: Mirko Viroli.
http://www.sato.kuis.kyoto-u.ac.jp/~igarashi/papers/pdf/variance.TOPLAS.pdf
Un gruppo di lavoro di Microsoft Cambridge ha recentemente proposto una soluzione applicata a C#: http://research.microsoft.com/~akenn/generics/ECOOP06.pdf