3 Stimmen

Ist es möglich, eine End-Run um Generika Kovarianz in C # < 4 in dieser hypothetischen Situation zu tun?

Angenommen, ich habe eine kleine Vererbungshierarchie von Animals:

public interface IAnimal {
    string Speak();
}

public class Animal : IAnimal {
    public Animal() {}
    public string Speak() {
        return "[Animal] Growl!";
    }
}

public class Ape : IAnimal {
    public string Speak() {
        return "[Ape] Rawrrrrrrr!";
    }
}

public class Bat : IAnimal {
    public string Speak() {
        return "[Bat] Screeeeeee!";
    }
}

Als Nächstes finden Sie eine Schnittstelle, die eine Möglichkeit bietet, die strings in IAnimals .

public interface ITransmogrifier<T> where T : IAnimal {
    T Transmogrify(string s);
}

Und schließlich gibt es noch eine Strategie, um das zu erreichen:

public class Transmogrifier<T> : ITransmogrifier<T> where T : IAnimal, new() {
    public T Transmogrify(string s) {
        T t = default(T);
        if (typeof(T).Name == s)
            t = new T();
        return t;
    }
}

Und nun die Frage. Ist es möglich, die mit [1], [2] und [3] gekennzeichneten Abschnitte so zu ersetzen, dass dieses Programm korrekt kompiliert und ausgeführt werden kann? Wenn man das nicht kann, ohne andere Teile als [1], [2] und [3] zu berühren, kann man dann trotzdem ein IAnimal aus jeder Instanz einer Transmogrifier in einer Sammlung, die beliebige Implementierungen eines IAnimal? Kann man eine solche Sammlung überhaupt erst einmal bilden?

    static void Main(string[] args) {
        var t = new Transmogrifier<Ape>();
        Ape a = t.Transmogrify("Ape");
        Console.WriteLine(a.Speak());  // Works!

        // But can we make an arbitrary collection of such animals?
        var list = new List<Transmogrifier< [1] >>() {
            // [2]
        };

        // And how about we call Transmogrify() on each one?
        foreach (/* [3] */ transmogrifier in list) {
            IAnimal ia = transmogrifier.Transmogrify("Bat");
        }
    }
}

4voto

Eric Lippert Punkte 628543

Lee hat Recht.

Wie Sie in Ihrer Frage andeuten, können Sie dies in C# 4 tun, indem Sie ITransmogrifier als kovariant ("out") in T markieren. List<ITransmogrifier<IAnimal>> und setzen eine Transmogrifier<Bat> in diese Liste aufzunehmen.

3voto

Lee Punkte 137913

Dies ist nicht möglich, da es keine Typbeziehung zwischen Transmorgifier<Ape> y Transmorgifier<Bat> also kann man sie nicht in dieselbe generische Liste aufnehmen (außer List<object> ). Die offensichtliche Lösung ist, einfach ITransmorgifier nicht-generisch:

public interface ITransmogrifier
{
    IAnimal Transmogrify(string s);
}

Dann könnten Sie

var list = new List<ITransmogrifier>() {
    new Transmorgifier<Ape>(), new Transmorgifier<Bat>() ...
};

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X