2010-05-20 10 views
11

(Me comprobarlo por mí mismo, pero no tengo VS2010 (aún))es C# 4.0 covariante Tupla

Decir que tengo 2 interfaces base:

IBaseModelInterface 
IBaseViewInterface 

Y 2 interfaces realizando aquellos:

ISubModelInterface : IBaseModelInterface 
ISubViewInterface : IBaseViewInterface 

Si defino un Tuple<IBaseModelInterface, IBaseViewInterface> me gustaría establecer que, basándose en el resultado de una fábrica que devuelve Tuple<ISubModelInterface, ISubViewInterface>.

En C# 3 no puedo hacer esto a pesar de que las interfaces secundarias se dan cuenta de las interfaces base. Y estoy bastante seguro de que C# 4 me permite hacer esto si estaba usando IEnumerable<IBaseModelInterface> porque ahora está definido con la palabra clave in para permitir la covarianza. Entonces, ¿me permite Tuple hacer esto?

Por lo que (poco) entiendo, la covarianza solo se permite en las interfaces, ¿eso significa que debe haber una interfaz ITuple<T1, T2>? ¿Existe esto?

+3

Tenga en cuenta que la palabra clave "in" permite * contravariancia *, no * covarianza *. IEnumerable está marcado como * out * porque * a T proviene de un IEnumerable *. –

Respuesta

12

Tuple es una clase (bueno, una familia de clases) - es invariante por definición. Como mencionará más adelante, solo las interfaces y los tipos de delegados soportan la varianza genérica en .NET 4.

No conozco la interfaz ITuple que yo sepa. Podría haber uno que sea covariante, ya que las tuplas son inmutables, por lo que solo obtendrás valores "fuera" de la API.

+0

Creo que podría definir mi propia IMyTuple que estaría bien – RichK

+1

@RichK - si tiene la intención de escribir su propia clase de tupla, debe examinar cuidadosamente la semántica de comparación e igualdad que ofrecen las tuplas de BCL. Supongo que desearía conservar el mismo comportamiento que se ofrece en las clases integradas, y parte de ese comportamiento no es obvio. – LBushkin

+2

@Jon Skeet, hay una interfaz 'ITuple' no genérica, pero es interna – smartcaveman

8

Puede heredar de tuple para crear su propia tupla covariante. De esta forma evitará tener que volver a escribir su propia lógica de igualdad.

public interface ICovariantTuple<out T1> 
{ 
    T1 Item1 { get; } 
} 
public class CovariantTuple<T1> : Tuple<T1>, ICovariantTuple<T1> 
{ 
    public CovariantTuple(T1 item1) : base(item1) { } 
} 

public interface ICovariantTuple<out T1, out T2> 
{ 
    T1 Item1 { get; } 
    T2 Item2 { get; } 
} 
public class CovariantTuple<T1, T2> : Tuple<T1, T2>, ICovariantTuple<T1, T2> 
{ 
    public CovariantTuple(T1 item1, T2 item2) : base(item1, item2) { } 
} 

etc.... for 3, 4, 5, 6, 7, 8 items 

Compilar Falla

Tuple<Exception> item = new Tuple<ArgumentNullException>(null); 

Compilar éxito

ICovariantTuple<Exception> item = new CovariantTuple<ArgumentNullException>(null); 

There is no base Tuple after 8 items, pero debería ser suficiente.

+0

¡Me gusta su solución con la interfaz y la clase complementaria! ¿Pero cuál es el propósito de la fábrica vacía? Creo que los dos últimos ejemplos muestran exactamente qué funciona y qué no. – MEMark