remarks - see cref c#
En C#4.0, ¿por qué un parámetro de salida en un método no puede ser covariante? (2)
Interesante. Sin embargo, en el nivel CLI no existe "salida", solo "ref"; hay un atributo que ayuda a los compiladores (para una tarea definida) que dice "no es necesario que lo pases".
Tal vez esta restricción se deba a que la CLI no tiene "out", solo "ref".
Dada esta interfaz mágica:
public interface IHat<out TRabbit>
{
TRabbit Take();
}
Y esta jerarquía de clase:
public class Rabbit { }
public class WhiteRabbit : Rabbit { }
Ahora puedo compilar esto:
IHat<WhiteRabbit> hat1 = null;
IHat<Rabbit> hat2 = hat1;
Lo cual es genial. Pero, ¿qué sucede si defino la interfaz de manera diferente?
public interface IHat<out TRabbit>
{
bool Take(out TRabbit r);
}
Estoy indicando que el sombrero puede estar vacío, usando un valor de retorno booleano por separado (la versión anterior quizás haya devuelto un conejo nulo de un sombrero vacío). Pero todavía estoy produciendo un conejo, por lo que no hago nada lógicamente diferente a la versión anterior.
El compilador C # 4.0 en el CTP da un error en la definición de la interfaz: requiere que los parámetros del método ''fuera'' sean de tipo invariante. ¿Existe alguna razón por la cual esto no está permitido o es algo que podría abordarse en una versión futura?
Aunque es un poco complicado, puede usar un contenedor de covarianza:
public class CovariantListWrapper<TOut, TIn> : IList<TOut> where TIn : TOut
{
IList<TIn> list;
public CovariantListWrapper(IList<TIn> list)
{
this.list = list;
}
public int IndexOf(TOut item)
{
// (not covariant but permitted)
return item is TIn ? list.IndexOf((TIn)item) : -1;
}
public TOut this[int index]
{
get { return list[index]; }
set { throw new InvalidOperationException(); }
}
public bool Contains(TOut item)
{
// (not covariant but permitted)
return item is TIn && list.Contains((TIn)item);
}
public void CopyTo(TOut[] array, int arrayIndex)
{
foreach (TOut t in this)
array[arrayIndex++] = t;
}
public int Count { get { return list.Count; } }
public bool IsReadOnly { get { return true; } }
public IEnumerator<TOut> GetEnumerator()
{
foreach (TIn t in list)
yield return t;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Insert(int index, TOut item) { throw new InvalidOperationException(); }
public void RemoveAt(int index) { throw new InvalidOperationException(); }
public void Add(TOut item) { throw new InvalidOperationException(); }
public void Clear() { throw new InvalidOperationException(); }
public bool Remove(TOut item) { throw new InvalidOperationException(); }
}
Esto le permite mantener la colección tal como se escribió originalmente y hacer referencia a ella de forma coherente sin crear una copia separada, de modo que las actualizaciones del original se vean en el uso covariante. Ejemplo:
class CovarianceWrapperExample
{
class Person { }
class Employee : Person { }
void ProcessPeople(IList<Person> people) { /* ... */ }
void Foo()
{
List<Employee> employees = new List<Employee>();
// cannot do:
ProcessPeople(employees);
// can do:
ProcessPeople(new CovariantListWrapper<Person, Employee>(employees));
}
}