Laden...

Forenbeiträge von cshw89 Ingesamt 4 Beiträge

24.11.2022 - 17:55 Uhr

@Palladin007: Ich denke, das war der entscheidene Hinweis, danke. Ich verstehe nur nicht, warum ich nicht auch Generics von Klassen als covariant definieren kann, da auch hier alles vom Typ T nur zurückgegeben wird, aber nicht geändert. Aber naja, mit einem zusätzlichen Interface funktioniert es dann auch mit OfType. Leider habe ich dadurch allerdings ein neues Problem mit Unity, da ich das Erstellen der Liste (GetList) nicht ohne weiteres im Unity Editor erledigen kann. Dann muss ich da mal weiterschauen. Trotzdem danke 🙂


using System.Linq;
using System.Collections.Generic;

public interface IElementAmount<out T> where T : ElementType {
	public T GetElementType();
}

public class ElementAmount<T> : IElementAmount<T> where T : ElementType {
    public readonly T Type;
    public readonly int Number;
    public ElementAmount(T type, int number) {
        Type = type;
        Number = number;
    }
    public T GetElementType() {
        return Type;
    }
}

public abstract class ElementType {
}
public class SpecialType : ElementType {
}
public class OtherType : ElementType {
}

public class Test {
    private SpecialHolder holder;
    
    public void Method() {
        var list = GetList();
        foreach (var amount in list.OfType<ElementAmount<SpecialType>>()) {
            holder.AddSpecialAmount(amount);
        }
    }
    
    public IList<IElementAmount<ElementType>> GetList() {
        var list = new List<IElementAmount<ElementType>>();
        list.Add(new ElementAmount<SpecialType>(new SpecialType(), 1));
        list.Add(new ElementAmount<OtherType>(new OtherType(), 1));
        return list;
    }
}

public class SpecialHolder {
    public void AddSpecialAmount(ElementAmount<SpecialType> amount) {
        /// add to list
    }		
}

@Th69: Normalerweise gebe ich dir recht. Allerdings möchte ich hier nicht ein Verhalten von ElementAmount ausführen, sondern Instanzen eines bestimmten Typs sammeln (siehe auch meine Lösung oben). SpecialHolder soll nur Instanzen vom Typ "ElementAmount<SpecialType>" sammeln. Im ursprünglichen Code wollte ich es nur so einfach wie möglich halten.

24.11.2022 - 16:20 Uhr

Es ist so ziemlich das gleich Problem, wie im Eingangspost. Ersetze nur die inneren Listen durch ElementAmount. Dadurch kann ich zusätzlich sicherstellen, dass der Cast wirklich immer funktioniert. Auch wieder in Java:


public static void main(String[] args) {
    List<ElementAmount<? extends ElementType>> lists = new ArrayList<>();
    lists.add(new ElementAmount<SpecialType>(mySpecialType, 1));
    lists.add(new ElementAmount<OtherType>(myOtherType, 2));

    ElementAmount<? extends ElementType> inner = lists.get(0);
    if (inner.Type instanceof SpecialType) {
        ElementAmount<SpecialType> special = (ElementAmount<SpecialType>) inner;
        DoSomethingWithSpecial(special);
    }
}

Unterschied zum Eingangspost, ich iteriere nicht über die komplette äußere Liste, sondern ermittle nur den ersten Eintrag.

Zum Thema Horror Kabinett. Es war ein simples Beispiel. Ein konkreteres Konstrukt, welches ich häufig benutze:


public static class TierHolder {
    Map<Class<? extends Tier>, List<? extends Tier>> tierListenAnhaengigVomTyp;
    
    public <T extends Tier> void addTier(T tier) {
        List<T> list = (List<T>) tierListenAnhaengigVomTyp.get(tier.getClass());
        if (list == null) {
            list = new ArrayList<>();
            tierListenAnhaengigVomTyp.put(tier.getClass(), list);
        }
        list.add(tier);
    }
    
    public <T extends Tier> List<T> getTiere(Class<T> clazz) {
        return (List<T>) tierListenAnhaengigVomTyp.get(clazz);
    }
    
    public static void main(String[] args) {
        TierHolder holder = new TierHolder();
        holder.addTier(new Katze());
        List<Katze> katzen = holder.getTiere(Katze.class);
    }
}

Durch die innere Struktur, kann ich bei TierHolder.getTiere immer davon ausgehen, dass mir eine konkrete Liste zurückgegeben wird. Natürlich habe ich hier zwei "gefährliche" Casts. Aber solange meine Klasse intern korrekt arbeitet, habe ich außen kein Problem.

24.11.2022 - 15:39 Uhr

Danke erstmal Abt. Properties kenne ich schon. Ich benutze sie auch schon an einigen Stellen, hier bisher aber noch nicht. Kann ich hier noch machen, das stimmt.

Java ist streng typsicher. Ich weiß nicht genau, was du meinst. Durch Upper/Lower-Bounds kann man den Typ in der Collection halt "verweichlichen", hat dafür aber auch weniger Möglichkeiten auf die Collection zuzugreifen. Hinzufügen von Elementen geht z.B. nicht. Durch die Bounds ist allerdings ein Cast auf einen Subtyp erlaubt. Wie könnte ich das folgende in C# umsetzen?


public static class Tier {
}
public static class Katze extends Tier {
}
public static class Hund extends Tier {
}

public static void main(String[] args) {
    List<List<? extends Tier>> lists = new ArrayList<>();
    lists.add(new ArrayList<Katze>());
    lists.add(new ArrayList<Hund>());
    
    List<? extends Tier> inner = lists.get(0);
    // inner.add(new Katze());  das geht natürlich nicht, aber...
    List<Katze> katzen = (List<Katze>) inner; // dieser Cast geht
    katzen.add(new Katze());
}

Bezüglich meinem Code, funktioniert die Lösung Enumerable.OfType leider nicht. Die Liste in Test.GetList wird gar nicht von mir befüllt, sondern von Unity. Dadurch habe ich nicht gemerkt, dass alle Einträge vom Typ "ElementAmount<ElementType>" sind, selbst wenn ich ein "SpecialType" dort auswähle. Dadurch kann ich die Elemente gar nicht vom Typ unterscheiden, sondern nur durch den Typ von "ElementAmount.Type".

23.11.2022 - 20:16 Uhr

Hallo zusammen,

ich experimentiere seit kurzem mit Unity, stoße aber immer wieder auf Probleme mit Generics, da ich aus meiner längeren Java-Vergangenheit andere Möglichkeiten gewohnt bin. Folgendes Problem habe ich gerade. Ich habe eine Klasse ElementAmount für verschiedene Typen, alle von der Basis-Klasse ElementType. Nun bekomme ich in "Test.Method" eine Liste mit ElementAmounts von "Test.GetList". Hier würde ich in Java schon "IList<ElementAmount<? extends ElementType>>" schreiben. Jetzt will ich aber für alle "ElementAmount<SpecialType>" eine spezielle Methode aufrufen. Beim Aufruf von "DoSomethingWithSpecial" kann ich aber nicht mal "auf eigene Gefahr" casten. Kann ich den Cast vielleicht doch erzwingen, oder ggf. den Rückgabewert von "GetList" ändern? Oder gibt es eine andere Möglichkeit, das Problem zu umgehen?


using System.Collections.Generic;

public class ElementAmount<T> where T : ElementType {
    public readonly T Type;
    public readonly int Number;
    public ElementAmount(T type, int number) {
        Type = type;
        Number = number;
    }
}

public abstract class ElementType {
}
public class SpecialType : ElementType {
}

public class Test {
    public void Method() {
        var list = GetList();
        foreach (var amount in list) {
            if (amount.Type is SpecialType specialType) {
                DoSomethingWithSpecial((ElementAmount<SpecialType>) amount);
            }
        }
    }
    
    public void DoSomethingWithSpecial(ElementAmount<SpecialType> amount) {
    }
    
    public IList<ElementAmount<ElementType>> GetList() {
        // ... fill list ...
        return new List<ElementAmount<ElementType>>();
    }
}

Vielen Dank
Kevin