Frage

diese magische Schnittstelle Gegeben:

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

Und diese Klassenhierarchie:

public class Rabbit { }

public class WhiteRabbit : Rabbit { }

Das kann ich jetzt kompilieren:

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

Das ist sehr gut. Aber was, wenn ich definieren die Schnittstelle unterschiedlich:

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

Ich bin darauf hinweisen, dass der Hut leer sein könnte, einen separaten boolean Rückgabewert mit (die vorherige Version vielleicht habe ein Null-Kaninchen aus einem leeren Hut zurückgegeben). Aber ich bin nur noch ein Kaninchen ausgeben, so dass nicht alles logisch anders als die Vorgängerversion zu tun.

Die C # 4.0-Compiler in der CTP gibt einen Fehler in der Schnittstellendefinition - es Methodenparameter eines invarianten Typs sein erfordert ‚out‘. Gibt es einen Hard-und-schnell Grund, warum dies nicht zulässig ist, oder ist es etwas, das in einer zukünftigen Version adressiert werden könnte?

War es hilfreich?

Lösung

Interessant. an der CLI-Ebene jedoch ist es nicht so etwas wie „out“ - nur „ref“; es ist ein Attribut, das Compiler (für eindeutige Zuordnung) hilft, die sagt: „Sie nicht passieren müssen, um es in“.

Vielleicht diese Einschränkung ist, weil die CLI "out" nicht hat, nur "ref".

Andere Tipps

Obwohl es ein bisschen umständlich ist, können Sie einen Kovarianz-Wrapper verwenden:

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

Auf diese Weise können Sie die Sammlung halten, wie es ursprünglich geschrieben wurde und kovariant darauf verweisen, ohne eine getrennte Kopie zu erstellen, so dass Aktualisierungen des Originals in der kovarianten Verwendung gesehen. Beispiel:

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));
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top