Question

Compte tenu de cette interface magique:

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

Et cette hiérarchie de classes:

public class Rabbit { }

public class WhiteRabbit : Rabbit { }

Je peux maintenant compiler ceci:

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

Ce qui est génial. Mais si je définis différemment l'interface:

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

J'indiquais que le chapeau peut être vide, en utilisant une valeur de retour booléen (la version précédente aurait peut-être retourné un lapin d'un chapeau nul vide). Mais je suis toujours sortie seulement un lapin, afin de ne pas faire quoi que ce soit logiquement différent à la version précédente.

Le compilateur C # 4.0 dans le CTP donne une erreur dans la définition de l'interface - il des paramètres de la méthode nécessite « out » pour être d'un type invariant. Y at-il une raison difficile et rapide pourquoi ce n'est pas autorisé, ou est-ce quelque chose qui pourrait être abordée dans une future version?

Était-ce utile?

La solution

Intéressant

. Cependant, au niveau de la CLI il n'y a pas une telle chose comme « out » - seulement « ref »; il est un attribut qui aide les compilateurs (pour l'affectation définitive) qui dit « vous n'avez pas besoin de passer dans ».

Peut-être que cette restriction est parce que la CLI ne pas "out", seulement "ref".

Autres conseils

Bien qu'il soit un peu embêtant, vous pouvez utiliser une enveloppe de covariance:

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(); }
}

Cela vous permet de garder la collection comme il a été saisi et faites référence covariante sans créer une copie détachée, de sorte que les mises à jour à l'original sont visibles dans l'utilisation covariant. Exemple:

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));
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top