Laden...

Wieso ist ein Int-Klassenmember initialisiert, obwohl im Konstruktor nichts zugewiesen wird?

Erstellt von R3turnz vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.581 Views
R
R3turnz Themenstarter:in
125 Beiträge seit 2016
vor 7 Jahren
Wieso ist ein Int-Klassenmember initialisiert, obwohl im Konstruktor nichts zugewiesen wird?

Hallo,
angenommen man hat folgendes:


int i;
int z = i + m;

I wäre hier nicht instanziert und würde somit einen Compilerfehler auslösen.
Mir ist gerade aufgefallen, dass in folgendem Fall i initialisiert ist:


public class Myclass
{
private int _i;
public int I {get; set;}
public Myclass()
  {
  }
}

Wenn ich nun auf I zugreife ist die Variable initialisiert, obwohl im Konstruktor nichts definiert ist. Bisher habe ich in leeren Konstruktoren immer allen Feldern ihren Standartwert zugewiesen. Kann ich das also einfach lassen?

LG

T
2.219 Beiträge seit 2008
vor 7 Jahren

Das liegt daran, dass int eigentlich ein Int32 Struct ist.
Structs sind Wertetypen, die immer initalisiert sind.
Int hat dabei den Default Wert 0
Hier solltest du dich mal mit den Werte- und Referenztypen beschäftigen.
Gehört passend dazu auch zu den Grundlagen 😃

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

R
R3turnz Themenstarter:in
125 Beiträge seit 2016
vor 7 Jahren

Mit der Zeit vergisst man auch die Basics 😉
Deklarierter Referenztyp => Verweiß auf null
Wertetyp => Kein Verweiß sondern Wert im Stack (Muss Wert haben)

LG

3.003 Beiträge seit 2006
vor 7 Jahren

Referenztypen sind auch immer intialisiert - mit null.

Automatisch initialisiert werden

  • Klassenmember (Instanzvariablen)
  • statische Variablen
  • Elemente von Arrays

Heisst im Umkehrschluss, dass lokale Variablen vor ihrer Verwendung intialisiert sein müssen, Klassenmember aber nicht.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

R
R3turnz Themenstarter:in
125 Beiträge seit 2016
vor 7 Jahren

Mir stellt sich hier noch eine letzte Frage:
Ich entnehme deiner Antwort Referneztypen und Wertetypen sind beide immer instanziert.(Mit null oder dem Standartwert) Meine Frage ist nun: Wieso wird bei einer lokalen Variablen, ein Compilerfehler ausgelöst? Im Debugger wird mir auch null bzw. der Standartwert angezeigt, genauso wie bei einem Klassenmember. (Diese wären dann ja schon instanziert)

3.003 Beiträge seit 2006
vor 7 Jahren

Hm, also, wenn du meiner Antwort DAS entnimmst, dann solltest du noch einmal lesen, was ich geschrieben habe.

Klassenmember, statische Variable und Array-Elemente sind immer initialisiert, dh. Deklaration und Initialisierung (Instanziierung macht überhaupt keinen Sinn, erst recht nicht bei Werttypen) sind bei diesen Variablentypen ein und derselbe Schritt.

Instanziiert und initialisiert sind zwei paar Schuhe.

Und genau deine Frage habe ich mit diesem Satz hier beantwortet:

Heisst im Umkehrschluss, dass lokale Variablen vor ihrer Verwendung intialisiert sein müssen, Klassenmember aber nicht.

Und wenn du im Debugger auf eine nicht initialisierte lokale Variable gehst, initialisiert der Debugger diese Variable. Was sollte er sonst mit dem Symbol machen? Dass das außerhalb des Debuggers nicht initialisiert ist, kannst du prüfen, indem du das hier versuchst, zu kompilieren:


static void Main()
{
    int myInt;
    if(myInt > 0) Console.WriteLine(myInt);
}

LaTino
Und Standard mit d, ist ja furchtbar.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

16.807 Beiträge seit 2008
vor 7 Jahren

.. und solche Basics sind täglich Brot als Entwickler. Inhalt der ersten 10 Seiten eines C# Buchs. Das ist essentiell für jede Zeile Code.
Das vergisst man nicht. Darf man nicht vergessen.

R
R3turnz Themenstarter:in
125 Beiträge seit 2016
vor 7 Jahren

Entschuldigt, aber ich bin immer noch nicht ganz durchgestiegen:

Referenztypen sind auch immer intialisiert - mit null.

heißt das:


string s; //ist das gleiche wie//
string s = null; 

Falls ja:Wieso wird beim einen ein Compilerfehler ausgelöst, beim anderen nicht?

Structs sind Wertetypen, die immer initalisiert sind.
Int hat dabei den Default Wert 0

heißt das:


int i; //entspricht//
int i = 0;

Auch an dieser Stelle wieder: Wieso beim ersten einen Compilerfehler?

Hoffe ich stehe nicht komplett auf dem Schlauch, danke für die Gedult.

F
10.010 Beiträge seit 2004
vor 7 Jahren

Du scheinst immer nur die hälfte zu lesen.

LaTino hat es dir extra nochmal rausgezogen und fett geschrieben.
Der Compilerfehler betrifft immer nur Functionslocale Variablen.

3.003 Beiträge seit 2006
vor 7 Jahren

Ich werd mich anstrengen, nicht sarkastisch zu werden 😉.

Die Bezeichnungen in jeder Programmiersprache - und das ist auch immer das erste, was man lernt - sind immer wie folgt:


class MyClass //Deklaration, "Bekanntmachung eines Symbols", Symbol ist hier: MyClass
{
    private int _myInt; //Deklaration der Klassenvariable (auch: Klassenmember oder Feld) _myInt

    public void MyMethod() //Deklaration des Symbols MyMethod als Methode und Klassenmember
    {
         int myOtherInt; //Deklaration einer lokalen Variable. Außerhalb ihres Gültigkeitsbereiches ist diese Variable nicht bekannt

         myOtherInt = 5; //Initialisierung, "erste Wertzuweisung"
    }
}

In einigen Programmiersprachen müssen auch als Klassenmember deklarierte Symbole initialisiert werden, bevor sie verwendet werden können. Das geschieht üblicherweise im Konstruktor.

In C# werden als Klassenmember deklarierte Symbole implizit initialisiert, also im Hintergrund durch die Magie des Compilers. Das macht der Compiler aber nur für Symbole, die als Member einer Klasse oder eines Structs deklariert wurden. Unter anderem sorgt das für wesentlich aufgeräumtere Konstruktoren.

Mit dem Thema Referenz vs. Wert-Typen hat das noch nichts zu tun. WENN der Compiler ein Symbol implizit initialisiert, tut er das mit dem sogenannten default-Wert. Dieser Wert ist hier definiert: C# Language Specification, der sehr kurze Abschnitt 5.2.

Die Variable myOtherInt im obigen Beispiel ist KEIN Klassenmember. Deshalb wird sie NICHT implizit initialisiert, sondern das muss der Programmierer machen. Und der Compiler ist so freundlich, und weigert sich, das Programm zu erstellen, wenn der Programmierer das mal vergessen hat.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

B
88 Beiträge seit 2016
vor 7 Jahren

Wow, danke für das ausführliche aufschlüsseln LaTino 👍 (auch wenn mir persönlich die Fakten jetzt im wesentlichen schon klar waren).

Mich würde allerdings noch interessieren warum nicht immer gleich verfahren wird, gibt es einen Grund dafür, dass lokale Variabeln nicht ebenfalls einfach implizit mit Standardwerten initialisiert werden?

Das Argument mit den aufgeräumteren Konstruktoren zieht bei mir für Klassenmember total, aber mir fällt kein Grund ein es bei lokalen Variabeln dann anders zu handhaben, übersehe ich etwas offensichtliches?

Zumal es zumindest der Debugger ja dann doch macht, wenn ich dich richtig verstanden habe.
[edit: Da hab ich zu schnell gelesen. Tut er ja nicht, deswegen kriegt man die Meldung ja auch beim Debuggen 😉]

Trotzdem, das unterschiedliche Vorgehen stiftet doch eigentlich nur Verwirrung?!

3.003 Beiträge seit 2006
vor 7 Jahren

Weil es zwischen lokalen Variablen und (wir beschränken uns mal darauf) Instanzvariablen Unterschiede gibt:

  • die Benutzung einer nicht initialisierten lokalen Variable kann leicht vom Compiler erkannt werden, und ist in 99% aller Fälle ein Fehler.

  • der Status einer Instanzvariable kann wegen ihres "globalen" (innerhalb der Instanz) Gültigkeitsbereichs nicht so leicht zur Compilezeit entdeckt werden und wäre auch in weniger Fällen ein Fehler.

  • Anzahl und Art von Instanzvariablen sind dem Speichermanagement zum Zeitpunkt der Instanziierung (durch new) bekannt). Er kann also den Speicher freiräumen: die Initialisierung (wenn du dir den erwähnten Abschnitt 5.2 anschaust) kommt "kostenlos", weil bei der Speicherreservierung einfach der entsprechende Bereich mit einem festen Wert (0) überschrieben - initialisiert - wird.

Für lokale Variablen wird der Bereich nicht freigeräumt, sondern erst bei der Initialisierung. Außerdem: schau dir das hier an:


void ExampleMethod()
{
    int a;

    //50 Zeilen Code
    a = 537;
   //weitere 50 Zeilen Code...
    return a * irgendwas;
}

Würde der Compiler die Variable initialisieren, und der Programmierer entfernt versehentlich die Zeile a = 537;, dann würde der Code noch funktionieren, obwohl gerade ein Bug eingebaut wurde. Und zwar einer, der sehr schwer zu finden ist.

Letzten Endes wäre es möglich gewesen, lokale Variablen mit ihrerm Defaultwert zu initialisieren. Man hat sich dagegen entschieden, weil man der Meinung war, dass man so einen fehlerfreieren Programmierstil erzwingt. Was auch nicht ganz falsch ist 😃.

LaTino
Edit: durch die Speicherreservierung bei der Instanziierung einer Klasse wäre es - soweit ich das verstehe - nicht einmal möglich, die Instanzvariablen uninitialisiert zu lassen. Die haben direkt einen Speicherbereich zugewiesen, und in dem steht immer auch irgendwas drin. Man kann nur dafür sorgen, dass klar ist, WAS drin steht.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

B
88 Beiträge seit 2016
vor 7 Jahren

Den zweiten Teil deines Postings hatte ich ungefähr schon so erahnt.

Über den ersten werde ich nochmal kurz nachdenken müssen (Speichermanagement usw.).
Danke auf jeden Fall schonmal für den "Schubs" 🙂