C# Generics: Liste mit Listen

captmcneil

Ensign
Registriert
Juni 2005
Beiträge
189
Hallo,

ich habe eigentlich eher mit Java zu tun, aber derzeit versuche ich privat, etwas mit .Net zusammenzubauen. Dabei fliege ich natürlich regelmäßig auf die Nase, weil ein paar Dinge eben in der einen Welt nicht so tun, wie in der anderen :)

In Java beruhen Generics ja auf diesem Type-Erasure-Ansatz, also sind nur für den Programmierer da, bringen zur Laufzeit aber absolut nichts. Dass das in C# anders ist, und es deshalb dort u.a. keine Wildcards gibt, habe ich mittlerweile verstanden. Allerdings scheint mir mein Anwendungsfall nicht so abnormal zu sein, als dass es hierzu keine vernünftige C#-Lösung geben kann.

Zum Problem:
Ich habe eine Liste mit Listen, wobei letztere beliebigen Typs sein sollen. In Java würde ich das ganze so schreiben:

Code:
List<List<?>> myListOfLists = new List<List<?>>();
myListOfLists.add(new ArrayList<String>());
myListOfLists.add(new ArrayList<Integer>());
...

in C# bekomme ich hier ein Problem, List<List<object>> tut natürlich nicht.

Das ganze lässt sich natürlich übertragen, wenn ich eine Liste mit generischen Interfaces haben möchte, was ja nun nicht so unüblich wäre:

Code:
interface MyGenericInterfaceIF<T> {
    T doCoolStuff();
}

public class CoolStringStuff implements MyGenericInterfaceIF<String> {
    public String doCoolStuff() {
        ....
    }
}


public class CoolIntegerStuff implements MyGenericInterfaceIF<Integer> {
    public Integer doCoolStuff() {
        ....
    }
}

...

public List<MyGenericInterfaceIF<?>> getCoolThings() {
    List<MyGenericInterfaceIF<?>> ret = new ArrayList<MyGenericInterfaceIF<?>>();
    ret.add(new CoolStringStuff());
    ret.add(new CoolIntegerStuff());
    return ret;
}

Wie würde man sowas in C# machen? Das Interface nicht generisch gestalten, macht ja den ganzen Vorteil von Generics kaputt, da ich die Rückgabe von doCoolStuff ja überall casten müsste. Gibt es nicht-generische Listen für sowas? Kann man was mit Arrays drehen? Ich brauch die Objekte nur in einer Form, wo die Reihenfolge fest ist.
 
Ich bin mir zwar nicht sicher ob ich dich richtig verstanden hab, aber ich glaub, dass was du suchst nennt sich ArrayList

PHP:
ArrayList myListOfLists = new ArrayList();

myListOfLists.Add(new List<string>());
myListOfLists.Add(new List<int>());
myListOfLists.Add(new List<object>());
myListOfLists.Add(new MyGenericInterfaceIF<int>());
myListOfLists.Add(new MyGenericInterfaceIF<string>());
 
Zuletzt bearbeitet:
Da Generics in C# typensicher sind, geht das einfach nicht. Woher soll der Compiler denn wissen ob T jetzt ein String oder ein Integer ist?
Die beste Lösung ist es eine List<object> zu verwenden. (ArrayList ist prinzipiell das gleiche, stammt allerdings noch aus dem .NET 1.1 Framework. List<object> ist einer ArrayList deshalb vorzuziehen.)
 
Okay, vielen dank.

Problematisch wäre dann eben das Auslesen dieser List<object> in einer For-Schleife, da ich ja nie weiß, auf was ich casten kann. Das hört sich für mich so an, als ob Generics hier eher nichts sind.
 
Zuletzt bearbeitet:
Wenn ich das richtig in Erinnerung habe, kannst du dir mit typeof den Typen holen und das Object in diesen Casten.
 
Wenn ich das richtig in Erinnerung habe, kannst du dir mit typeof den Typen holen und das Object in diesen Casten.
Ich verstehe nicht ganz, was das bringen soll...

Code:
List<object> listOfGenerics = ... ;
foreach (object eachGeneric in listOfGenerics)
{
    // ..?
}
 
Zuletzt bearbeitet:
Du musst doch vorher wissen was für Typen möglich sind?!
Dann prüfst du eben welcher Typ es ist und castest entsprechend bzw arbeitest damit weiter.
 
Was da typeof bringen soll versteh ich auch nicht ganz, aber ich würs so machen:

PHP:
            List<object> lists = new List<object>();
            List<string> strings = new List<string>();
            List<int> numbers = new List<int>();

            strings.AddRange(new string[] {"eins","zwei","drei"});
            numbers.AddRange(new int[] { 1, 2, 3 });

            lists.Add(strings);
            lists.Add(numbers);

            foreach (ICollection list in lists)
            {
                if (list is List<string>)
                {
                    foreach (string s in list)
                    {
                        Console.WriteLine(s);
                    }
                }
                else if (list is List<int>)
                {
                    foreach (int i in list)
                    {
                        Console.WriteLine(i);
                    }
                }
            }
 
In dem Beispiel mit Listen mag das noch halbwegs überschaubar aussehen, weil List ein ungenerisches Interface ICollection implementiert. Im Fall von List<object> mit object = MyGenericInterfaceIF<...> sieht das schlecht aus - was schreibst du da in das foreach? :). Und lass das mal 20 Typen sein, die da drin sein könnten, den if-else-Block will ich nicht jedesmal ausimplementieren müssen, wenn ichs brauche ;)

Aber das wäre eine Idee, dass MyGenericInterfaceIF von einem ungenerischen Interface ableitet, auf das ich immer casten kann, wenn mich der Typ des Generics nicht interessiert - sowas hab ich gesucht ;)

Code:
interface IMyInterface
{
    object DoCool();
}

interface IMyGenericInterface<T> : IMyInterface
{
    new T DoCool();
}

...

void test()
{
    IMyGenericInterface<int> theObj1 = ... ;
    IMyGenericInterface<string> theObj2 = ... ;
    List<IMyInterface> theList = new List<IMyInterface>();
    theList.Add(theObj1);
    theList.Add(theObj2);

    foreach (IMyInterface eachObj in theList)
    {
        Console.Out.WriteLine(eachObj.DoCool());
    }
}

Problem ist hier halt, dass ich die Schnittstelle quasi 2x implementieren muss; ich kann den Rückgabetyp "object" nicht präzisieren auf T. Mal schauen ob ich da noch was finde...
 
Zuletzt bearbeitet:
Ich glaube die Lösung deines Problems ist es, eine Sprache zu verwenden, die dynamische Typisierung unterstützt. Mit Boo müsste das z.B. gehen.
 
Zurück
Oben