myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Grundlagen von C# » Type Parameter, der ein Interface implementieren muss
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Type Parameter, der ein Interface implementieren muss

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen


Garzec ist offline

Type Parameter, der ein Interface implementieren muss

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,
ich versuche ein ganz minimalistisches Entity Component System aufzubauen. Ich könnte etwas fertiges nehmen, aber wollte es mal selbst versuchen. Zumal ich am Ziel bin, sobald diese Frage geklärt ist.

Ich erstelle ein Dictionary, das als Komponenten Pool dient. Dieser Pool hat als Key die ID der Entität (Entitäten sind bei mir nur GUIDs) und als Value die Komponente. Somit stellt jeder Pool einen einzelnen Komponententypen dar. Übergeordnet sollen die Pools in einer "Collection" liegen, damit man über diese Collection anhand des Komponententyps und der ID der Entität sehr schnell an die Komponente kommt, um Schleifen zu vermeiden.

Nun gibt es bereits die fertige Klasse KeyedByTypeCollection, die anscheinend darauf achten wird, dass jeder Pool in dieser Auflistung einzigartig bleibt.

Jede Komponente muss das Interface IComponent implementieren. Daher kann ich beim Hinzufügen, Suchen und Entfernen von Pools generische Typen erstellen, die das Interface erfordern.

C#-Code:
        private KeyedByTypeCollection<Dictionary<Guid, IComponent>> componentPools = new KeyedByTypeCollection<Dictionary<Guid, IComponent>>();

        public Dictionary<Guid, TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
        {
            return componentPools[typeof(TComponent)];
        }

        public void AddComponentPool<TComponent>() where TComponent : IComponent
        {
            componentPools.Add(new Dictionary<Guid, TComponent>());
        }

        public void RemoveComponentPool<TComponent>() where TComponent : IComponent
        {
            componentPools.Remove(typeof(TComponent));
        }

Nun ergibt sich das Problem, dass ich nicht

C#-Code:
KeyedByTypeCollection<Dictionary<Guid, IComponent>>

schreiben kann, sondern eigentlich sowas haben müsste

C#-Code:
KeyedByTypeCollection<Dictionary<Guid, Type where Type : IComponent>>

Hat jemand eine Idee, wie ich das Ganze fixen könnte?

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Garzec am 09.01.2019 22:47.

09.01.2019 22:44 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.179
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Versteh nicht ganz, worauf Du hinaus willst.

Aber wenn jede Komponente das Interface implementieren muss, dann passt doch

C#-Code:
KeyedByTypeCollection<Dictionary<Guid, IComponent>>

Wieso solltest Du das nicht schreiben können? verwundert Was genau meinst Du damit?

Oder willst Du auf sowas raus?

C#-Code:
    public class MyBag<TComponent> where TComponent : IComponent
    {
        private KeyedByTypeCollection<Dictionary<Guid, TComponent>> componentPools = new KeyedByTypeCollection<Dictionary<Guid, TComponent>>();
    }
09.01.2019 23:01 Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Mit dem aktuellen Code erhalte ich aber 2 Fehler

Zitat:
GetComponentPool: Cannot implicitly convert IComponent to TComponent

Zitat:
AddComponentPool: Cannot convert from TComponent to IComponent

und da weiß ich nicht, wie ich das Ganze dann fixen muss.
10.01.2019 07:39 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ThomasE. ThomasE. ist männlich
myCSharp.de-Mitglied

avatar-178.gif


Dabei seit: 26.11.2013
Beiträge: 444
Entwicklungsumgebung: Visual Studio 2015Pro/2017Ent


ThomasE. ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

so wie ich das verstehe, versuchst du das Dictionary mit einem anderen Typen zu erstellen als es vorgegeben ist: TComponent

C#-Code:
// Vorgabe:
KeyedByTypeCollection<Dictionary<Guid, IComponent>>
// Versuch:
new KeyedByTypeCollection<Dictionary<Guid, TComponent>>()

Grüße
10.01.2019 08:55 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@Thomas

Das habe ich leider schon probiert. Aber es ist ja auch logisch, wieso es nicht funktioniert. TComponent ist ja nur ein generischer Typparameter, den ich bei den Methoden definiert habe. Außerhalb der Methoden ist diese Variable natürlich nicht vorhanden.
10.01.2019 09:48 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ThomasE. ThomasE. ist männlich
myCSharp.de-Mitglied

avatar-178.gif


Dabei seit: 26.11.2013
Beiträge: 444
Entwicklungsumgebung: Visual Studio 2015Pro/2017Ent


ThomasE. ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

ok hab mich falsch ausgedrückt, es war eine Feststellung daß das wegen den unterschiedlichen Typen nicht funktionieren kann.

Die Lösung müßte anders ausehen.
Wie, kann ich leider nicht sagen, da ich mom. viel am Hut hab...

Grüße
10.01.2019 09:52 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also ich werde den EntityManager wahrscheinlich nicht statisch machen, weil statisch ist ja eigentlich böse ...

Aber ich dachte mir ich zeige einfach mal den Code, weil er sehr kurz ist, vielleicht verschafft das einen guten Überblick.

C#-Code:
    internal interface IComponent
    {
    }

    internal interface ISystem
    {
        void Run();
    }

    internal static class EntityManager
    {
        private static List<Guid> activeEntities = new List<Guid>();

        public static IReadOnlyList<Guid> ActiveEntities { get { return activeEntities; } }

        private static KeyedByTypeCollection<Dictionary<Guid, IComponent>> componentPools = new KeyedByTypeCollection<Dictionary<Guid, IComponent>>();

        public static KeyedByTypeCollection<ISystem> Systems { get; } = new KeyedByTypeCollection<ISystem>();

        public static Guid CreateEntity()
        {
            Guid entityId = Guid.NewGuid();
            activeEntities.Add(entityId);
            return entityId;
        }

        public static void DestroyEntity(Guid entityId)
        {
            activeEntities.Remove(entityId);

            for (int i = 0; i < componentPools.Count; i++)
            {
                componentPools[i].Remove(entityId);
            }
        }

        public static Dictionary<Guid, TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
        {
            return componentPools[typeof(TComponent)];
        }

        public static void AddComponentPool<TComponent>() where TComponent : IComponent
        {
            componentPools.Add(new Dictionary<Guid, TComponent>());
        }

        public static void RemoveComponentPool<TComponent>() where TComponent : IComponent
        {
            componentPools.Remove(typeof(TComponent));
        }

        public static void AddComponentToEntity(Guid entityId, IComponent component)
        {
            componentPools[component.GetType()].Add(entityId, component);
        }

        public static void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
        {
            componentPools[typeof(TComponent)].Remove(entityId);
        }
    }

Momentan liegt es nur noch am oben genannten Problem. Aber ich hätte gerne eine Liste von Pools, die darauf achtet, dass jeder Pool einzigartig ist und die Pools selbst halten zur GUID (EntityID) die dazugehörige Komponente.

Durch die Dictionaries möchte ich einfach lange Suchen vermeiden.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Garzec am 10.01.2019 10:09.

10.01.2019 10:08 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
MarsStein MarsStein ist männlich
myCSharp.de-Poweruser/ Experte

avatar-3191.gif


Dabei seit: 27.06.2006
Beiträge: 3.130
Entwicklungsumgebung: VS 2013, MonoDevelop
Herkunft: Trier -> München


MarsStein ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

ThomasE. hat völlig recht. Was Du da machst, kann so nicht funktionieren.

C#-Code:
KeyedByTypeCollection<Dictionary<Guid, IComponent>>

hier sagst Du, dass Deine Collection Dictionary<Guid, IComponent> enthält.

C#-Code:
Dictionary<Guid, TComponent> GetComponentPool<TComponent>() where TComponent : IComponent

hier willst DU aber Dictionary<Guid, TComponent> zurückgeben. Daher der erste Konvertierungsfehler. Hier müsstest Du auch Dictionary<Guid, IComponent> zurückgeben.

C#-Code:
componentPools.Add(new Dictionary<Guid, TComponent>());

genauso müsstest Du hier nicht Dictionary<Guid, TComponent> erstellen und hinzufügen sondern ebenfalls ein new Dictionary<Guid, IComponent>(). Daher der zweite Konvertierungsfehler.

Ob dann der Zweck so wie gedacht weiterhin erfüllt wird und/oder der Indexer von KeyedByTypeCollection richtig funktioniert, kann ich so nicht sagen.

Gruß, MarsStein

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von MarsStein am 10.01.2019 10:59.

10.01.2019 10:58 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Danke für deine Hilfe, deine genannten Fehler sind mir bereits aufgefallen, aber ich weiß nicht, ob ich einen Dictionary Cast erzwingen muss und ob dieser überhaupt so möglich ist. Ich ging davon aus, dass er bei where TComponent : IComponent weiß, dass dieses Element vom Typ des Interfaces ist.

Ich scheue mich aber ein wenig vor einem Cast, da dies wahrscheinlich etwas unperformant ist, von daher hat vielleicht noch jemand eine einfachere Idee? Damit meine ich einen ganz anderen Weg.
10.01.2019 11:14 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.179
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Wenn Deine Collection verschiedene Implementierungen von IComponent halten soll, dann kommst Du so oder so nicht um einen Cast herum; dann brauchst aber eigentlich kein generischen Ansatz.
TComponent über eine generische Einschränkung wiederum nimmt nur einen einzigen Typ an, der IComponent implementiert.
10.01.2019 11:24 Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@Abt

Ok, ich würde es dann casten. Aber könntest du deine anderen Sätze noch weiter erläutern? Wenn ich den generischen Typen nehme, habe ich die Möglichkeit genau vorzugeben, dass nur etwas rein darf, was das Interface implementiert und eben diesen angegebenen Typen auch wieder rauszubekommen.

Als Beispiel hier mal ein Pseudo Movement System, wie das Ganze dann hinterher abläuft

C#-Code:
    internal class Movement : ISystem
    {
        public void Run()
        {
            for (int i = 0; i < EntityManager.ActiveEntities.Count; i++)
            {
                Guid entityId = EntityManager.ActiveEntities[i];

                if (EntityManager.GetComponentPool<Position>().TryGetValue(entityId, out Position positionComponent))
                {
                    if (EntityManager.GetComponentPool<MovementSpeed>().TryGetValue(entityId, out MovementSpeed movementSpeedComponent))
                    {
                        positionComponent.X += movementSpeedComponent.Value;
                        positionComponent.Y += movementSpeedComponent.Value;
                    }
                }
            }
        }
    }
10.01.2019 12:15 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.179
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Garzec:
Wenn ich den generischen Typen nehme, habe ich die Möglichkeit genau vorzugeben, dass nur etwas rein darf, was das Interface implementiert und eben diesen angegebenen Typen auch wieder rauszubekommen.

Pauschal: nein.

Wenn sich der generische Ansatz auf die Klasse bezieht, dann kann die Instanz dazu auch nur einen einzigen Typen halten => konkreter Typ.
Bezieht sich der generische Ansatz auf eine Methode, dann kann darin natürlich mehr landen - hast aber keine konkrete Typen mehr.

Dein Entity Manager ist nach Zweitem implementiert.

Ich bin mir nicht sicher, ob ich wirklich bei dem Ansatz getrennte Pools machen würde... sehe absolut keinen Vorteil.
10.01.2019 12:22 Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Du meinst du würdest einen großen Pool mit sämtlichen Komponenten halten und wenn du alle Positions Komponenten haben willst, danach filtern? Also alle Dictionary Values vom Typ Position?
10.01.2019 13:17 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.179
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ja, da ich keinen Vorteil in der Trennung sehe - selbst auf technischer Ebene nicht.
10.01.2019 14:22 Beiträge des Benutzers | zu Buddylist hinzufügen
Garzec Garzec ist männlich
myCSharp.de-Mitglied

Dabei seit: 02.05.2016
Beiträge: 50
Entwicklungsumgebung: Visual Studio
Herkunft: Gießen / Hessen

Themenstarter Thema begonnen von Garzec

Garzec ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Bei der Performance bin ich mir unsicher, ob ein permanentes Filtern eines großen Dictionaries schneller wäre, als ein Casten eines kleineren Dictionaries.
10.01.2019 14:28 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
witte
myCSharp.de-Mitglied

Dabei seit: 03.09.2010
Beiträge: 826


witte ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ein Dictionary hat einen O(1)-Aufwand bei Zugriff auf ein Objekt. Am Besten mal die Aufwandsklassen anschauen.
10.01.2019 19:34 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.179
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Garzec:
Bei der Performance bin ich mir unsicher,

premature optimization is the root of all evil

Zitat von Garzec:
als ein Casten eines kleineren Dictionaries.

Casten musst so oder so - das ist es ja; jedenfalls so, wie Deine Architektur hier ist.
Der Unterschied is nur die Zugriffsgröße, siehe Kommentar von witte.
10.01.2019 19:39 Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 10 Monate.
Der letzte Beitrag ist älter als 10 Monate.
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 19.11.2019 04:09