Objekt verwerfen

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von EaranMaleasi.

    Objekt verwerfen

    Hallo zusammen,

    vielleicht kann mir ja Jemand von euch weiterhelfen. Folgende Situation:

    Ich empfange Objekte des Typs ServerProtokoll<T> über ein Socket, welche ich im Nachhinein auswerten möchte. Dafür gibt es eine API, welche mir diese Daten vom Server holen soll.

    Hier mal eine Funktion daraus:

    C#-Quellcode

    1. public event EventHandler MitarbeiterlisteChanged;
    2. private MitarbeiterCollection _mitarbeiterliste = new MitarbeiterCollection();
    3. /// <summary>Ruft alle aktiven Mitarbeiter ab.</summary>
    4. public MitarbeiterCollection Mitarbeiterliste
    5. {
    6. get => _mitarbeiterliste;
    7. private set
    8. {
    9. this._mitarbeiterliste = value;
    10. OnMitarbeiterlisteChanged(EventArgs.Empty);
    11. }
    12. }
    13. private void LadeAktiveMitarbeiter()
    14. {
    15. ServerProtokoll<ObjectNachricht> serverProtokoll = new ServerProtokoll<ObjectNachricht>(new ObjectNachricht(ObjektType.MitarbeiterAktiv));
    16. Mitarbeiter[] result = new Mitarbeiter[] { };
    17. void handler(object sender, Mitarbeiter[] e)
    18. {
    19. result = e;
    20. ewh.Set();
    21. }
    22. GlobaleVariabeln.srv.MitarbeiterReceived += handler;
    23. serverProtokoll.Send(Connection);
    24. ewh.WaitOne(Timeout);
    25. GlobaleVariabeln.srv.MitarbeiterReceived -= handler;
    26. Mitarbeiterliste = new MitarbeiterCollection(result);
    27. }


    Wenn nun die Mitarbeiter durch einen anderen Client aktualisiert werden, dann sendet der Server automatisch eine neue Nachricht an alle Clienten, die dafür sorgt, dass die Mitarbeiterliste aktualisiert wird. Nun kann es ja vorkommen, dass der Client gerade eine Schleife auf die Liste anwendet.

    Typischer Fall wäre hier z.B.

    C#-Quellcode

    1. foreach(Mitarbeiter in Mitarbeiterliste)
    2. {
    3. }


    Würde ich nun die Liste ändern, dann würde spätestens jetzt mein Client abstürzen.

    Ich könnte nun entweder immer die Liste vorher kopieren, oder mithilfe eines locks blockieren, doch dann müsste ich dies ja an jeder Stelle machen. Gibt es hier eine Möglichkeit das nur an einer Stelle direkt zu ändern? Ich könnte mir die Liste natürlich auch jedes mal vom Server laden lassen, doch das wäre Aufgrund der Größe des Objekts eher suboptimal.

    Am besten wäre es, wenn eine Änderung der liste direkt dazu führt, dass aktuell laufende Schleifen quasi wiederholt werden.

    Habt Ihr eine Idee, wie sich das am besten realisieren lässt?
    LG
    Ich könnte mir iwie eine ConcurrentQueue<T> vorstellen, in die Quasi "Neuigkeiten vom Server" abgelegt werden.
    Turnusmässig wird in diese Liste geguckt, ob Neuigkeiten da sind, und wenn ja, werden diese abgearbeitet, bis die Liste wieder leer ist.

    Andere Frage: Warum hast du diesen eigenartigen MitarbeiterListe-Datentyp?
    .Net verfügt über recht viele tw. enorm leistungsfähige generische Auflistungstypen, dasses mir noch nie untergekommen ist, eine so spezielle Auflistung erstellen zu müssen.
    Wenn man so anfängt endet man bei zig Auflistungstypen, die im Grunde alle dasselbe machen.
    Und damit hat man zig-mal denselben Code - Grütze, anners gesagt.

    ErfinderDesRades schrieb:

    Ich könnte mir iwie eine ConcurrentQueue<T> vorstellen, in die Quasi "Neuigkeiten vom Server" abgelegt werden.
    Turnusmässig wird in diese Liste geguckt, ob Neuigkeiten da sind, und wenn ja, werden diese abgearbeitet, bis die Liste wieder leer ist.


    Das ist eine sehr gute Idee! Ich werde mich gleich mal dran setzen.

    ErfinderDesRades schrieb:


    Andere Frage: Warum hast du diesen eigenartigen MitarbeiterListe-Datentyp?
    .Net verfügt über recht viele tw. enorm leistungsfähige generische Auflistungstypen, dasses mir noch nie untergekommen ist, eine so spezielle Auflistung erstellen zu müssen.
    Wenn man so anfängt endet man bei zig Auflistungstypen, die im Grunde alle dasselbe machen.
    Und damit hat man zig-mal denselben Code - Grütze, anners gesagt.


    Die MitarbeiterCollection hilft mir dabei, schneller auf Mitarbeiter zugreifen zu können. Zuvor muss ich im Code immer mit LINQ Abfragen ob in List<Mitarbeiter> ein Mitarbeiter mit der ID xx oder dme Usernamen xzy ist. Die MitarbeiterCollection bietet mir dagegen zwei Accessoren, womit ich mir direkt den Mitarbeiter über die ID, bzw. den Usernamen holen kann. Meiner Meinung nach ist es so einfacher bestimmte Dinge in meinem Mitarbeiter-Array zu überprüfen, ohne dass ich immer und immer wieder die selben LINQ-Abfragen auf mein Array beziehen muss.

    C#-Quellcode

    1. public class MitarbeiterCollection : List<Mitarbeiter>, IEnumerable<Mitarbeiter>
    2. {
    3. private readonly Mitarbeiter[] arr;
    4. public int Length => arr.Length;
    5. /// <summary>
    6. /// Erstellt eine neue, leere MitarbeiterCollection
    7. /// </summary>
    8. public MitarbeiterCollection()
    9. {
    10. arr = new Mitarbeiter[] { };
    11. }
    12. /// <summary>
    13. /// Erstellt eine neue MitarbeiterCollection für alle übergebenen Mitarbeiter.
    14. /// </summary>
    15. /// <param name="mitarbeiter">Ein Array an Mitarbeitern</param>
    16. public MitarbeiterCollection(Mitarbeiter[] mitarbeiter)
    17. {
    18. this.arr = (mitarbeiter == null) ? new Mitarbeiter[] { } : mitarbeiter;
    19. }
    20. /// <summary>
    21. /// Erstellt eine neue MitarbeiterCollection für alle in der Liste vorhandenen Mitarbeiter.
    22. /// </summary>
    23. /// <param name="mitarbeiter">Eine List des Typs Mitarbeiter.</param>
    24. public MitarbeiterCollection(List<Mitarbeiter> mitarbeiter)
    25. {
    26. this.arr = (mitarbeiter == null) ? new Mitarbeiter[] { } : mitarbeiter.ToArray();
    27. }
    28. /// <summary>
    29. /// Ruft ein Mitarbeiter Objekt anhand seines Usernamens ab.
    30. /// </summary>
    31. /// <param name="username">Der Username den der Mitarbeiter haben muss.</param>
    32. /// <returns>Wenn gefunden, dann ein Objekt des Typs Mitarbeiter, ansonsten null.</returns>
    33. public Mitarbeiter this[string username]
    34. {
    35. get
    36. {
    37. for (int i = 0; i < arr.Length; i++)
    38. {
    39. if (arr[i].USERNAME == username)
    40. return arr[i];
    41. }
    42. return null;
    43. }
    44. }
    45. /// <summary>
    46. /// Ruft ein Mitarbeiter Objekt anhand seiner USER_ID ab.
    47. /// </summary>
    48. /// <param name="user_id">Die USER_ID des Mitarbeiters.</param>
    49. /// <returns>Wenn gefunden, dann ein Objekt des Typs Mitarbeiter, ansonsten null.</returns>
    50. public new Mitarbeiter this[int user_id]
    51. {
    52. get
    53. {
    54. for (int i = 0; i < arr.Length; i++)
    55. {
    56. if (arr[i].USER_ID == user_id)
    57. return arr[i];
    58. }
    59. return null;
    60. }
    61. }
    62. /// <summary>
    63. /// Prüft ob der Mitarbeiter derzeit online ist.
    64. /// </summary>
    65. /// <param name="userID">Die USER_ID des Mitarbeiters</param>
    66. /// <returns></returns>
    67. public bool IstOnline(int userID)
    68. {
    69. return Contains(userID) ? this[userID].IsOnline : false;
    70. }
    71. /// <summary>
    72. /// Prüft ob der Mitarbeiter derzeit online ist.
    73. /// </summary>
    74. /// <param name="username">Der Username des Mitarbeiters.</param>
    75. /// <returns></returns>
    76. public bool IstOnline(string username)
    77. {
    78. return Contains(username) ? this[username].IsOnline : false;
    79. }
    80. /// <summary>
    81. /// Liefert die Anzahl der Mitarbeiter, die derzeit online sind.
    82. /// </summary>
    83. /// <returns></returns>
    84. public int SindOnline()
    85. {
    86. int online = 0;
    87. foreach (Mitarbeiter mitarbeiter in arr)
    88. {
    89. if (mitarbeiter.IsOnline)
    90. online++;
    91. }
    92. return online;
    93. }
    94. /// <summary>
    95. /// Prüft ob ein Mitarbeiter existiert.
    96. /// </summary>
    97. /// <param name="userID">Die USER_ID des Mitarbeiters.</param>
    98. /// <returns></returns>
    99. public bool Contains(int userID)
    100. {
    101. for (int i = 0; i < arr.Length; i++)
    102. {
    103. if (arr[i].USER_ID == userID)
    104. return true;
    105. }
    106. return false;
    107. }
    108. /// <summary>
    109. /// Prüft ob ein Mitarbeiter existiert.
    110. /// </summary>
    111. /// <param name="username">Der Username des Mitarbeiters.</param>
    112. /// <returns></returns>
    113. public bool Contains(string username)
    114. {
    115. for (int i = 0; i < arr.Length; i++)
    116. {
    117. if (arr[i].USERNAME.ToUpper() == username.ToUpper())
    118. return true;
    119. }
    120. return false;
    121. }
    122. public IEnumerator GetEnumerator() => arr.GetEnumerator();
    123. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    124. }
    Hätte ich gewusst, zu was mein Vorschlag wird...

    Erstens. Du erbst von List<T>. Deine Collection ist eine List. Du brauchst kein interners Array:
    (Das hatte ich eigentlich im Sinn mit den Indexern die ich vorgeschlagen hatte)


    Zweitens. Deine Collection besitzt bereits sämtliche Funktionen, die eine List<T> ebenfalls besistzt, einschließlich der LINQ-Erweiterungen:


    Drittens. Siehst du dort die AddRange Funktion? Darüber kannst du alles der Liste hinzufügen, was IEnumerable<T> Implementiert, wozu auch Arrays gehören.
    Contains() gibt es ebenfalls, wobei dein Contains eher als FirstOrDefault() zu verstehen ist. Sachen wie IstOnline usw. kannst du natürlich gerne als Funktion in deiner Collection Implementieren, wenn du es wirklich so oft aufrufst.
    Hallo EaranMaleasi,

    vielen Dank für deine außführliche Erläuterung. Ich habe meine Collection nun entsprechend angepasst. Jetzt wo ich mir deine Erklärung nochmals genauer angesehen habe, ergibt es schon direkt mehr Sinn.

    Anbei noch eine Frage:
    Ich habe noch zusätzlich die Klasse

    C#-Quellcode

    1. class NachrichtenQueue : List<object>


    Da ich ja nun von List<object> ableite, ist die Queue ja direkt eine List des Typs Object. Nun möchte ich aber beim hinzufügen der Liste noch zusätzliche Dinge erledigten. Allerdings kann ich die Add Methode nicht überschrieben. Ich möchte nämlich jedes mal, wenn ich ein Item hinzufüge meine Queue starten.

    Hast du eine Idee, wie ich das erreiche?



    LG
    Der NachrichtenQueue soll vom Server empfangene Nachrichten verarbeiten.

    Wenn vom Server eine Nachricht empfangen wurde, dann wird diese mittels .Add() zur Liste hinzugefügt. Sobald ein item zur Liste hinzugefügt, soll die Liste damit anfangen diese in einer Schleife durchzugehen, sofern diesnicht in diesen Moment schon geschieht. Sprich Wenn ich Add() aufrufe, soll Methode ListeAbarbeiten() aufgerufen werden. Diese Methode loopt dann alle Objekte meine Liste durch und prüft dementsprechend den Inhalt.

    LG

    Gonger96 schrieb:

    Warum? Queue erbt von IEnumerable also kann ich auch per foreach enumerieren. Was soll da passieren?


    Bringt die App zum crashen, wenn du im Foreach bist und über einen anderen Thread ein Item hinzufügst oder entfernst. STichwort Collection Changed Exception.

    @EaranMaleasi

    War relativ simple umzusetzen.

    C#-Quellcode

    1. class NachrichtenQueue : Queue
    2. {
    3. private void QueueAbarbeiten()
    4. {
    5. while (true)
    6. {
    7. Thread.Sleep(1);
    8. while (this.Count > 0)
    9. {
    10. object obj = this.Dequeue();
    11. Console.WriteLine(obj);
    12. }
    13. }
    14. }
    15. public NachrichtenQueue()
    16. {
    17. Thread tmp = new Thread(QueueAbarbeiten);
    18. tmp.IsBackground = true;
    19. tmp.Start();
    20. }
    21. }