Pregunta

Dada esta interfaz mágico:

public interface IHat<out TRabbit>
{
    TRabbit Take();
}

Y esta jerarquía de clases:

public class Rabbit { }

public class WhiteRabbit : Rabbit { }

Ahora puedo compilar este:

IHat<WhiteRabbit> hat1 = null;
IHat<Rabbit> hat2 = hat1;

¿Qué es grande. Pero lo que si defino la interfaz diferente:

public interface IHat<out TRabbit>
{
    bool Take(out TRabbit r);
}

Estoy indicando que el sombrero esté vacío, utilizando un valor de retorno booleano (la versión anterior sería tal vez han vuelto un conejo nula de un sombrero vacío). Pero estoy siendo sólo la salida de un conejo, por lo que lógicamente no hacer nada diferente a la versión anterior.

El compilador C # 4.0 en el CTP da un error en la definición de interfaz - requiere 'hacia fuera' parámetros del método a ser de un tipo invariante. ¿Hay alguna razón dura y rápida por qué esto no está permitido, o se trata de algo que podría ser abordado en una versión futura?

¿Fue útil?

Solución

Interesante. Sin embargo, a nivel de la CLI no hay tal cosa como "fuera" - solamente "ref"; no es un atributo que ayuda a los compiladores (para la asignación definitiva) que dice que "no es necesario para pasarlo en".

Tal vez esta restricción se debe a que la CLI no tiene "fuera", sólo "ref".

Otros consejos

A pesar de que es un poco de una molestia, puede utilizar un envoltorio 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 como si se hubiera escrito originalmente y se refieren a ella covariantly sin crear una copia individual, por lo que las actualizaciones de los originales se ven 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));
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top