Vererbungsproblem ?

  • C#

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    Vererbungsproblem ?

    Hallo

    ich habe in meiner Anwendung eine Class, in der lagern 3 Sturcturs im ersten wird eine Liste vom Zweiten generiert und in dem wiederum eine liste vom Dritten.

    nun gib ich von der Form an die Class einen Wert, die gibt den wert dann weiter an den Structur, der gibt ein teil der werte weiter. dort wird wieder einteil der Informationen an den 3. weiter gegeben.

    wenn ich das alles auswerte, dann hat sich der wert aber nicht geändert ... hat jm. eine Idee?

    Code ...


    Spoiler anzeigen

    C#-Quellcode

    1. public partial class ManagerTeacher : Form
    2. {
    3. public ManagerTeacher()
    4. {
    5. InitializeComponent();
    6. }
    7. private teacher tc;
    8. private void ManagerTeacher_Load(object sender, EventArgs e)
    9. {
    10. tc = new teacher();
    11. nu_lession.Maximum = 10;
    12. nu_lession.Value = 1;
    13. cb_gender.Items.AddRange(teacher.getGender());
    14. cb_gender.SelectedIndex = 0;
    15. cb_lessionstate.Items.AddRange(teacher.getLessioState());
    16. cb_lessionstate.SelectedIndex = 0;
    17. cb_day.Items.AddRange(teacher.getDaysOfWeekStrg());
    18. cb_day.SelectedIndex = 0;
    19. cb_lessionstate.SelectedIndexChanged += new EventHandler(cb_lessionstate_SelectedIndexChanged);
    20. cb_day.SelectedIndexChanged += new EventHandler(cb_lessionstate_SelectedIndexChanged);
    21. nu_lession.ValueChanged += new EventHandler(cb_lessionstate_SelectedIndexChanged);
    22. ls_timetable.Columns.Add(new ColumnHeader() { Text = "Hour" });
    23. ls_timetable.Columns.AddRange(teacher.getDaysOfWeekStrg().Select(x => new ColumnHeader() { Text = x }).ToArray());
    24. setListView();
    25. }
    26. private int lastNum;
    27. private void cb_lessionstate_SelectedIndexChanged(object sender, EventArgs e)
    28. {
    29. int state = cb_lessionstate.SelectedIndex;
    30. int hour = (int)nu_lession.Value - 1;
    31. int day = cb_day.SelectedIndex;
    32. tc.setDayLession(day, hour, state);
    33. if (hour == 9 && lastNum == 8 && day < teacher.getDaysOfWeekStrg().Length-1)
    34. {
    35. nu_lession.Value = 1;
    36. cb_day.SelectedIndex = day + 1;
    37. }
    38. if(hour == 0 && lastNum == 1 && day > 0)
    39. {
    40. nu_lession.Value = 10;
    41. cb_day.SelectedIndex = day - 1;
    42. }
    43. if (sender is ComboBox)
    44. if (((ComboBox)sender).Name == cb_lessionstate.Name)
    45. nu_lession.Value = (hour + 2);
    46. lastNum = hour;
    47. setListView();
    48. }
    49. private void setListView()
    50. {
    51. List<teacher.Day> days = tc.getDays();
    52. Func<int, string[]> getListView = (hour) => new string[6].Select((x, n) => n == 0
    53. ? (hour+1).ToString()
    54. : days[n - 1].getLessions()[hour].getState().ToString()).ToArray();
    55. ls_timetable.Items.Clear();
    56. ls_timetable.Items.AddRange(new ListViewItem[10].Select(((x, n) => new ListViewItem(getListView(n)))).ToArray());
    57. }
    58. }

    Spoiler anzeigen

    C#-Quellcode

    1. public class teacher
    2. {
    3. private List<Day> days;
    4. public enum LessionState
    5. {
    6. In_class, Available
    7. }
    8. }
    9. public struct Day
    10. {
    11. public DayOfWeek today { get; private set; }
    12. private List<Lession> lessions;
    13. public Day(DayOfWeek day)
    14. {
    15. today = day;
    16. lessions = new Lession[10].Select((x, n) => new Lession(n)).ToList();
    17. }
    18. public void setTeachLessionAt(int index, string teach)
    19. {
    20. lessions[index].setTeach(teach);
    21. }
    22. public void setLessionStateAt(int index, LessionState state)
    23. {
    24. lessions[index].setState(state);
    25. }
    26. public List<Lession> getLessions()
    27. {
    28. return lessions;
    29. }
    30. }
    31. public struct Lession
    32. {
    33. public int hour { get; private set; }
    34. private LessionState lessionstate;
    35. public string teach { get; set; }
    36. public Lession(int hour)
    37. {
    38. this.hour = hour;
    39. lessionstate = LessionState.Available;
    40. teach = string.Empty;
    41. }
    42. public void setTeach(string teach)
    43. {
    44. this.teach = teach;
    45. }
    46. public void setState(LessionState state)
    47. {
    48. lessionstate = state;
    49. }
    50. public LessionState getState()
    51. {
    52. return lessionstate;
    53. }
    54. }
    55. public teacher()
    56. {
    57. days = new Day[5].Select((x, n) => new Day(getDaysOfWeek()[n])).ToList();
    58. }
    59. public void setDayLession(int day, int lession,int state)
    60. {
    61. LessionState stateL = (LessionState)Enum.Parse(typeof(LessionState), state.ToString());
    62. days[day].setLessionStateAt(lession, stateL);
    63. }
    64. public bool DayExist(Day today)
    65. {
    66. return days.Exists(x => x.today == today.today);
    67. }
    68. public List<Day> getDays()
    69. {
    70. return days;
    71. }
    72. public static string[] getLessioState()
    73. {
    74. return Enum.GetNames(typeof(LessionState)).Select(x => x.Replace('_', ' ')).ToArray();
    75. }
    76. public static string[] getDaysOfWeekStrg()
    77. {
    78. return getDaysOfWeek().Select(x => x.ToString()).ToArray();
    79. }
    80. private static DayOfWeek[] getDaysOfWeek()
    81. {
    82. DayOfWeek[] week = (DayOfWeek[])Enum.GetValues(typeof(DayOfWeek));
    83. return new DayOfWeek[5].Select((x,n) => week[n +1]).ToArray();
    84. }
    85. }

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Facebamm“ ()

    Strukturen sind PassByValue, wenn du sie also mit Parametern übergibst oder gleich setzt, dann erzeugst du eine Kopie dieser Struktur und nicht wie bei Klassen ein Verweis auf ein Objekt. Mit dieser Zeile z.B. ​lessions[index].setTeach(teach); bewirkst du nix, denn ​lessions[index] gibt dir eine Kopie der Lession Struktur aus der Liste bei der du dann die Werte setzt. Du musst diese also wieder zurück in die Liste schreiben. ​lessions[index] = lessions[index].setTeach(teach);
    Mehr Informationen zu dem Thema:
    Werte in einer List(of) einer private structure ändern
    Veränderbare Wertetypen sind böse ;)
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @~blaze~
    Man muss halt wissen, was man macht.

    Was üblicherweise das Problem an der Sache ist ;)
    Siehe die beiden Links im verlinkten Post:
    ericlippert.com/2011/03/14/to-box-or-not-to-box/
    ericlippert.com/2008/05/14/mutating-readonly-structs/
    Plus der hier:
    blog.coverity.com/2014/05/21/enumerator-advance/

    Es gibt sehr viele Situationen, in denen Dinge passieren, die man nicht erwartet hätte. Um das zu vermeiden sollte man von vornherein Wertetypen immutable gestalten (bzw. andersrum eine Klasse verwenden, wenn es mutable sein soll). Es kann gute Gründe geben, es anders zu machen, aber das sollte man sich gut überlegen (siehe dritter Link).
    Davon abgesehen sind Wertetypen vom Konzept her anders als Referenztypen. Ich beziehe mich immer gerne auf das Beispiel mit dem Stuhl (siehe verlinkter Post).
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Niko Ortner Jou.
    @Facebamm Mit anderen Worten:
    Mach aus den Strukturen Klassen und instanziiere sie ordentlich mit New. Das sollte Deine Probleme lösen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Niko Ortner schrieb:

    Es gibt sehr viele Situationen, in denen Dinge passieren, die man nicht erwartet hätte. Um das zu vermeiden sollte man von vornherein Wertetypen immutable gestalten


    Ich meine damit, dass man ein gutes Gespür für sowas entwickeln sollte. Es pauschal als böse darzustellen oder wirklich "immer immutable" zu propagieren, halte ich für übertrieben. Man sollte halt immer im Kopf behalten, wie Strukturen verarbeitet werden.

    Viele Grüße
    ~blaze~
    Hast Du ein paar gute Beispiele für Situationen, bei denen veränderbare Wertetypen sinnvoll sind?
    (Ich frage aus Interesse, nicht zur Argumentation)
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    ~blaze~ schrieb:

    "immer immutable" zu propagieren, halte ich für übertrieben.
    Korrekt.
    Ich verwende Strukturen im Prinzip nur da, wo ich auch 3 einzelne Variablen elementaren Typs hinschreiben kann, das macht sich insbesondere dann gut, wenn sonst viele Parameter an eine Prozedur übergeben werden müssten, und alle 3 Wochen ein weiterer Parameter hinzukäme.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Sorry, dass es jetzt so Offtopic wird, aber ich antworte da jetzt schon noch drauf.

    Möglich ist es z.B. um geringfügige Änderungen durchzuführen. Prädestiniert für diese Art der Anwendung ist ein rekursives Programm, bei dem der Parameter von einem Aufrufer mit einer Änderung weitergeleitet wird.
    Z.B.:

    C#-Quellcode

    1. public struct ValueStack<T>
    2. private object _parent;
    3. private T _value;
    4. public bool IsEmpty
    5. {
    6. get { return _parent == null; }
    7. }
    8. public T Value {
    9. get { return _value; }
    10. set {_value = value; }
    11. }
    12. public void Push()
    13. {
    14. Push(default(T));
    15. }
    16. public void Push(T nextInitialValue)
    17. {
    18. _parent = this;
    19. _value = value;
    20. }
    21. public T Pop()
    22. {
    23. if (IsEmpty)
    24. throw new InvalidOperationException("The stack is empty.");
    25. T v = _value;
    26. this = (ValueStack<T>)_parent;
    27. return v;
    28. }
    29. }

    Das ist eine meiner "Lieblingsanwendungen", auch wenn sie nur in wenigen Fällen sinnvoll ist. Ich finde die Struktur einfach seltsam gewitzt. ;)

    Die Idee ist simpel:
    - Der Stack wird über Boxing (Wertetyp, der in ValueType oder Object abgelegt wird und daher über eine Referenz abgelegt wird, einer von Nikos Links enthält genaueres darüber) erzeugt, indem der _parent ebenfalls eine ValueStack<T>-Instanz erhält. D.h. das Verhalten ist quasi rekursiv.
    - Value hält das aktuell oben liegende Item, das noch nicht fest im Stack verankert wurde
    - Push fügt den aktuellen Wert in den parent und setzt ggf. den initialValue als Wert der Value-Eigenschaft, die aber weiterhin geändert werden kann
    - Pop ersetzt das oben aufliegende Item mit dem letzten Element des Stacks
    - IsEmpty gibt an, ob der Stack bereits Elemente enthält, ignoriert dabei aber Value, da das nicht fest verankert ist

    Nachteilig ist, dass das Boxing zeitaufwändig ist, es funktioniert ja ähnlich wie das Anlegen der Instanz einer Klasse. Stack<T> ist da halt einfach eleganter, es hält alle Elemente in einem Array.
    Man beachte, dass der Stack nie verändert wird.

    Warum geht's nicht mit Klassen? Alles, außer der Value-Eigenschaft lässt sich mit Klassen und Verkettung (Stack-Element zeigt je auf das Vorgänger-Stack-Element) einwandfrei modellieren. Würde die Value-Eigenschaft einer Instanz geändert, würde sie natürlich auch für alle Verweise anders sein. Diesen Nachteil hat man bei ValueStack<T> nicht.

    Eine häufiger anzutreffende Sache wäre bspw. ein Vektor, der als struct modelliert werden sollte. X und Y kann man als öffentliche Eigenschaften/Felder nach außen geben, manchmal möchte man nur eine Koordinate ändern. Jedes mal den kompletten Konstruktor aufzurufen und sämtliche Parameter setzen zu müssen, ohne dass sich was ändert, wäre viel Arbeit und eigentlich auch ineffizient. Bei einer Matrix ists noch schlimmer und man merkt auch schnell, dass es wahnsinnig unübersichtlich ist.

    Strukturen, bei denen ich tatsächlich Felder/Eigenschaften immutable mache, sind i.a. zusammengeschweißte Werte, d.h. Werte bei denen eine Abhängigkeit voneinander besteht (Wert1 impliziert eine gewisse Eigenschaft von Wert2) oder die Werte einfach zusammengehören (man betrachte bspw. eine Form und ein Steuerelement darauf).
    Das Unelegante ist halt, dass durch das stete Vorhandensein des Default-Konstruktors die Validität der Strukturen nicht gewährleistet ist. Oftmals sind ja sämtliche Werte in einer Struktur gültig, sodass man keine Sonderfälle definieren kann.

    Viele Grüße
    ~blaze~