Domanda

Data questa interfaccia magica:

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

E questa gerarchia di classe:

public class Rabbit { }

public class WhiteRabbit : Rabbit { }

Ora posso compilare questo:

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

Che è grande. Ma cosa succede se io definisco l'interfaccia in modo diverso:

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

sto indicando che il cappello potrebbe essere vuoto, con un valore di ritorno booleano separato (la versione precedente sarebbe forse tornato un coniglio da un cappello nulla vuoto). Ma io sono ancora solo l'output un coniglio, in modo da non fare nulla logicamente diversa dalla versione precedente.

Il compilatore C # 4.0 in CTP dà un errore nella definizione dell'interfaccia - richiede 'out' parametri del metodo ad essere di tipo invariante. C'è una ragione hard-e-veloce perché questo non è consentito, o è qualcosa che potrebbe essere risolto in una versione futura?

È stato utile?

Soluzione

Interessante. Tuttavia, a livello CLI non esiste una cosa come "fuori" - solo "ref"; v'è un attributo che aiuta compilatori (per l'assegnazione definitiva) che dice "non c'è bisogno di passare in".

Forse questa restrizione è perché il CLI non ha "fuori", solo "ref".

Altri suggerimenti

Anche se è un po 'di fastidio, è possibile utilizzare un wrapper di 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(); }
}

Ciò consente di mantenere la collezione come era stato originariamente digitato e si riferiscono ad esso covariantly senza creare una copia indipendente, in modo che gli aggiornamenti l'originale sono visti nell'uso covariante. Esempio:

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));
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top