Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

1

13.05.2016, 22:43

[C#] Klasse soll alle Instanzen von sich selbst speichern

Hi Leute,

ich habe mehrere Klassen (auch Generics), die alle während der Laufzeit erzeugten Instanzen in einer statischen Liste speichern. Diese Liste möchte ich am allerbesten direkt in der Klasse speichern und nicht in einer Verwaltungsklasse!

Damit ich das nur einmal Programmieren muss habe ich mir folgende Basisklasse geschrieben:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface IInstance
{
    // Eigenschaften
    string InstanceID { get; }
}
public abstract class InstanceLister<T> : ObservableObject, IInstance where T : IInstance
{
    // Eigenschaften
    public static ObservableHashSet<T> Instances { get; private set; }

    public abstract string InstanceID { get; }

    // Konstruktor
    static InstanceLister()
    {
        Instances = new ObservableHashSet<T>();
    }
    public InstanceLister()
    {
        Instances.Add(this);
    }
    ~InstanceLister()
    {
        Instances.Remove(this);
    }

    // Methoden
    public static bool ExistsID(string InstanceID)
    {
        return Instances.FirstOrDefault(i => i.InstanceID == InstanceID) != null;
    }
}
Mein Compiler meckert bei den beiden this folgendes: "Konvertierung von "ACALC.Backend.InstanceLister<T>" in "T" nicht möglich." Warum kann er das nicht umwandeln? T ist doch ein IInstance und InstanceLister<T> ist auch ein IInstance!?

EDIT: Bevor wer fragt: ObservableHashSet ist eine eigene Kreation. Kreuzung aus ObservableCollection und HashSet. :)
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »CeDoMain« (13.05.2016, 22:57)


Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

2

13.05.2016, 23:29

T ist nicht konkret spezifiziert. Es könnte entweder ein IInstance sein, es könnte ein InstanceListener<T> sein, es könnte aber auch ein MyFancyInstance : IInstance sein. Die Liste im 3. Fall könnte keine InstanceListener<T> Instanzen aufnehmen.
Du musst schon ObservableHashSet<InstanceLister<T>> oder ObservableHasSet<IInstance> verwenden.

Aber, auch wenn du keine "Verwaltungsklasse" haben willst, solltest du dennoch kein solches Konstrukt bauen. Wofür brauchst du die Instanzen denn alle in der gleichen Liste?
(Mal ganz abgesehen davon, dass der Inhalt beliebig von außen angepasst werden kann. ;) )
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

3

14.05.2016, 00:54

Stimmt, wo dus sagst, fällt mir auch auf, dass ich beliebige Klassen, die IInstace implementieren als T übergeben kann - soll natürlich nicht so sein! Ich habe das jetzt mal abgeändert (IInstance brauche ich noch für was andres). Das Programm Compiliert so auch. Danke schonmal. :)

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface IInstance
{
    // Eigenschaften
    string InstanceID { get; }
}
public abstract class InstanceLister<T> : ObservableObject, IInstance where T : InstanceLister<T>
{
    // Eigenschaften
    public static ObservableHashSet<T> Instances { get; private set; }

    public abstract string InstanceID { get; }

    // Konstruktor
    static InstanceLister()
    {
        Instances = new ObservableHashSet<T>();
    }
    public InstanceLister()
    {
        Instances.Add((T)this);
    }
    ~InstanceLister()
    {
        Instances.Remove((T)this);
    }
    // [...]
}


Dann habe ich das mal ausprobiert, indem ich eine Klasse A davon abgeleitet und eine Instanz erstellt habe. Dabei gab es in der Add-Methode des ObservableHashSet eine NullReferenceException.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
public class ObservableHashSet<T> : HashSet<T>, INotifyCollectionChanged
{
    // [...]
    public new bool Add(T Item)
    {
        var r = base.Add(Item);
        CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, Item));
        return r;
    }
    // [...]
}
Der Fehler wird von Zeile 7 geworfen. Zeile 6 wird ordnungsgemäß ausgeführt - habe ich überprüft - die Liste wird um das Element erweitert. In der Variablen Item stehen sehr viele Null-Werte. Das ist verständlich, denn der Konstruktor von A ruft als erstes den des InstanceLister auf und dieser wiederum die obige Methode. Zu diesem Zeitpunkt ist noch kein Element der Klasse A initialisiert. Die Instanz aber schon angelegt, denn base.Add(Item) hat ja funktioniert. Was ist also das Problem!? Kann mir das jemand beantworten?

EDIT: Weil du danach gefragt hast: Diese Klasse wird unter anderem in meinem Pin-System (Event Based Components) verwendet. Diese Instanzliste soll ein Pool darstellen, mit dessen Elementen sich verknüpft werden kann. Letztendlich werde ich dieses System noch woanders verwenden, weil es auch da einen Pool gibt...
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »CeDoMain« (14.05.2016, 01:01)


CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

4

14.05.2016, 01:22

Argh ist das ein dummer Fehler gewesen. :dash:

Ich habe CollectionBased anstatt OnCollectionBased aufgerufen. Ersteres ruft den EventHandler direkt auf - dieser ist natürlich null, weil sich noch keiner auf diesen Event registriert hat... Sorry!

Bleibt jetzt nur noch zu klären, ob mein Code da oben korrekt ist (der des InstanceLister) und ob es andere bessere Arten gibt einen solchen Pool zu implementieren. :)
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

5

14.05.2016, 01:49

Hi Leute,

ich habe mehrere Klassen (auch Generics), die alle während der Laufzeit erzeugten Instanzen in einer statischen Liste speichern.

Wieso?

6

14.05.2016, 01:57

Genau, weshalb brauchst du soetwas? Da steckt sicher ein Designfehler hinter.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

7

14.05.2016, 09:21

Diese Frage habe ich auch schon gestellt, aber sie wurde wohl ignoriert...
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

8

14.05.2016, 13:28

EDIT: Weil du danach gefragt hast: Diese Klasse wird unter anderem in meinem Pin-System (Event Based Components) verwendet. Diese Instanzliste soll ein Pool darstellen, mit dessen Elementen sich verknüpft werden kann. Letztendlich werde ich dieses System noch woanders verwenden, weil es auch da einen Pool gibt...
Ist das keine Antwort auf die Frage? Es gibt Klassen, die können sich mit anderen verbinden, dazu müssen sie wissen welche Instanzen verfügbar sind. Das mache ich mit meinem InstanceLister. :)
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

9

14.05.2016, 16:00

Gut, das hatte ich wohl übersehen... (aber scheinbar war ich auch nicht der einzige)

Zwischen den "Pins" sollen also Verbindungen hergestellt werden. Dann soll denen auch von außen mitgeteilt werden, zu welchem Pin die Verbindung hergestellt werden soll. Ich wüsste nicht, in wie weit da eine statische Liste aller Instanzen, die von der Klasse selbst geführt wird, relevant sein sollte.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

10

14.05.2016, 16:27

Ich bin gerade dabei eine Lösung für das Event Based Components Problem zu schaffen. Ich habe mir heute eine Matrix programmiert, die jede Kombination von Input- und OutputPins eines Werttyps (GenericParameter) mit einer CheckBox darstellt. Damit das mit dem GenericParameter klappt, wird vom ViewModel der Matrix je nach gewähltem Werttyp ein Generische Controllerklasse erzeugt. Diese generiert dann mit folgender Methode die Matrix:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class InputPin<T> : InstanceLister<InputPin<T>>, IInputPin where T : Value, new() { }
public class OutputPin<T> : InstanceLister<OutputPin<T>>, IOutputPin where T : Value, new() { }

public class ControllerViewModel<T> : ObservableObject, IControllerViewModel where T : Value, new()
{
    // Eigenschaften
    public ObservableCollection<OutputPinViewModel> RowHeaders { get; set; }
    public ObservableCollection<InputPinViewModel> ColumnHeaders { get; set; }
    public ObservableCollection<ConnectionViewModel> MatrixElements { get; set; }

    // Konstruktor
    public ControllerViewModel()
    {
        RowHeaders = new ObservableCollection<OutputPinViewModel>();
        ColumnHeaders = new ObservableCollection<InputPinViewModel>();
        MatrixElements = new ObservableCollection<ConnectionViewModel>();

        InputPin<T>.Instances.CollectionChanged += Pins_CollectionChanged;
        OutputPin<T>.Instances.CollectionChanged += Pins_CollectionChanged;

        GenerateMatrix();
    }

    // Methoden
    private void Pins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        GenerateMatrix();
    }
    private void GenerateMatrix()
    {
        // Reset
        RowHeaders.Clear();
        ColumnHeaders.Clear();
        MatrixElements.Clear();
            
        foreach (var ip in InputPin<T>.Instances)
        {
            // Spaltenüberschriften erzeugen
            ColumnHeaders.Add(new InputPinViewModel(ip));
        }
            
        int Row = 0;
        foreach (var op in OutputPin<T>.Instances)
        {
            // Zeilenüberschriften erzeugen
            RowHeaders.Add(new OutputPinViewModel(op));

            // Matrixeinträge erzeugen
            int Column = 0;
            foreach (var ip in InputPin<T>.Instances)
            {
                MatrixElements.Add(new ConnectionViewModel<T>(Row, Column, ip, op));
                Column++;
            }
            Row++;
        }
    }
}
Die View der Matrix bindet dann an die drei ObservableCollections und zeigt die Matrix an. Das Häkchensetzen funktioniert auch schon - einzig am Layout muss ich noch polieren. Im Anhang ist ein Bild. Meiner Meinung nach ist der Code verständlich genug... ;)

Im Augenblick habe ich das Problem, dass wenn ich das Programm schließe, der Destruktor der InstanceLister Klasse aufgerufen wird, der wiederum die Instanz aus Instances löscht. Das führt (wegen den im obigen Code registrierten Events) zu einer Neubrechnung der Matrixelemente. Da bekomme ich folgende Fehlermeldung in Zeile 31:

Zitat

Ausnahme ausgelöst: "System.NotSupportedException" in PresentationFramework.dll

Zusätzliche Informationen: Von diesem CollectionView-Typ werden keine Änderungen der "SourceCollection" unterstützt, wenn diese nicht von einem Dispatcher-Thread aus erfolgen.
Was heißt das? Wenn ich im Netz nach dieser Meldung suche, dann bekomme ich zahlreiche Informationen über Backgroundworker und OCs in mehreren Threads. Dass der Destruktor aus einem anderen Thread aufgerufen wird, mag ja sein, aber was kann ich schon dagegen tun? :(
»CeDoMain« hat folgendes Bild angehängt:
  • Screenshot.png
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

Werbeanzeige