Laden...

Implizierter Cast bei Listen (und anderen Datenstrukturen) nicht möglich? [==> Kovarianz-Problem]

Erstellt von Briefkasten vor 10 Jahren Letzter Beitrag vor 10 Jahren 1.336 Views
Briefkasten Themenstarter:in
446 Beiträge seit 2004
vor 10 Jahren
Implizierter Cast bei Listen (und anderen Datenstrukturen) nicht möglich? [==> Kovarianz-Problem]

Hallo,

ich bin anscheinend in C# ein bisschen eingerostet was Generizitäten und Casts betrifft.

Für meine Klasse:

    public class Vertex<W,D> : IVertex<W,D>
        where W : IComparable<W>
    {

habe ich eine Extension geschrieben:

public static IEnumerable<IVertex<W, D>> Depth_First_Traversal<W, D>(this IVertex<W, D> s, IList<IVertex<W, D>> visited)
            where W : IComparable<W>
        {

Nun möchte ich diese Extension aufrufen was mir nur mit der Angabe vom Typ IVertex gelingt:


            Vertex<int, object> v1 = new Vertex<int, object>();
            v1.Depth_First_Traversal<int, object>(new List<IVertex<int, object>>());

Aber eigentlich sollte das doch auch so gehen:

            Vertex<int, object> v1 = new Vertex<int, object>();
            v1.Depth_First_Traversal<int, object>(new List<Vertex<int, object>>());

was aber in einem Fehler resultiert:> Fehlermeldung:

Error 3 Argument 2: cannot convert from 'System.Collections.Generic.List<Get.DataStructure.Vertex<int,object>>' to 'System.Collections.Generic.IList<Get.DataStructure.IVertex<int,object>>'

Ich kann beim Parameter "IList visited" auch eine Liste übergeben (welche ja das Interface implementiert). Geht mein vorhaben bei Listen nicht? Falls ja, gibt es dafür ein Workaround?

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

2.078 Beiträge seit 2012
vor 10 Jahren

Das was du haben willst, geht nur, wenn der generische Typ-Parameter mit einem out versehen ist.

Das würde den generischen Typ-Parameter als kovariant kennzeichnen, was bei IList allerdings nicht der Fall ist.

Einen Workaround gibt es meines Wissens nach nicht.

Briefkasten Themenstarter:in
446 Beiträge seit 2004
vor 10 Jahren

Danke, das hat mir schon weitergeholfen.

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

Briefkasten Themenstarter:in
446 Beiträge seit 2004
vor 10 Jahren
Hinweis von herbivore vor 10 Jahren

Beitrag von Datenstruktur flexibel gestalten hierher verschoben, da es weiterhin um das Kovarianz-Problem geht ==> Text entsprechend angepasst.

Aufgrund des obigen Beitrags von Palladin007 habe ich das Gefühl, dass mein Ansatz nicht so toll war.

Ich hatte mir folgendes Schichten-model vorgestellt:

1.) IDatenstruktur
2.) Datenstrukturklasse die grundlegende Methoden des Interfaces implementiert

3.) Business Model
4.) Erweiterte Datenstrukturklasse die auf Business Model angepasst ist.

Um die Datenstruktur flexibel zu halten, ist die unterste Schicht mit Generizitäten ausgestattet.
Umso höher man in der Schicht ist, umso weniger Generizitäten kann man setzen, da die obersten Schichten ein spezielles Problem abdecken.

Hier ein etwas aufwändigeres Beispiel, das die Problematik aber besser illustriert.

Die unterste Schicht:


    public interface IEdge<W, T, D> : IData<D>
        where W : IComparable<W>
        where T : IVertex<W, D>
    {
        W Weight { get; set; }
        T U { get; set; }
        T V { get; set; }
    }

W - Das Gewicht kann sich je nach Anwendungsfall unterscheiden (int, float, usw)
D - In diese Generizität kann eine x-beliebige Information gespeichert werden. (IData<D>)
T - Falls man eine erweiterte Datenstruktur zusammen bauen will.

 public interface IVertex<W,D> : IData<D>
        where W : IComparable<W>  
    {

        W Weight { get; set; }
        IEnumerable<IEdge<W, IVertex<W, D>, D>> Edges { get; set; }

Die Basisklasse der Datenstruktur sieht dann so aus:

  public class Edge<W, D> : IEdge<W, D>
        where W : IComparable<W>
    {

Diese Datenstruktur soll, in einem Projekt verwendet werden, das ein Schienennetz abbildet. Es gibt daher folgende Klassen "Rail" (Gleis - speichert Informationen zu Zustand, Länge, Materialtyp usw.) und "Connection" (Verbindet mehrere Gleise).

Daraus ergeben sich folgende erweiterte Datenstrukturen (ConnectionVertex und RailEdge)

    public class ConnectionVertex : Vertex<double, Connection>
    {
        public ConnectionVertex()
        {
            this._Edges = new ObservableCollection<RailEdge>(); 
        }
    }
    public class RailEdge : Edge<double, Rail>{}

Die Datenstruktur wurde Schicht für Schicht präzessiert. Algorithmen sind auf den Graphen weiterhin anwendbar, da diese sich auf IEdge und IVertex beziehen. Einzig allein das Kovarianz Problem scheint nicht lösbar zu sein.

Den aus den MSDN Link geht hervor, dass Typen mit dem out Modifizierer keinen getter oder setter haben dürfen. Somit kann ich diesen weder beim Gewicht noch anders wo hier verwenden.

Habe ich in diesem Fall die Constrains ebenfalls missbraucht wie herbivore in Datenstruktur flexibel gestalten meinte? Wie kann ich diese Grundsatz Idee am Besten realisieren? Komplett auf Generizitäten verzichten?

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp