C# – Die Falle mit dem this[]-Indexer und dem Enum

Vor ein paar Tagen kam ein Kollege auf mich zu und meinte, er habe beim Unit-Test schreiben einen Fehler in meinem Code entdeckt. Um mir den Fehler zu zeigen, hat er mir eigens einen Unit-Test geschrieben, welcher in etwa so aussah:

// MyTypeList : List<string>
MyTypeList list = new MyTypeList {"a", "b", "c"};

if(list.ElementAt(0) == null)
{
    throw new NullReferenceException();
}

if(list[0] == null)
{
    // Hier knallt er!
    throw new NullReferenceException();
}

if(list[1] == null)
{
    throw new NullReferenceException();
}

Er knallte immer, wenn man auf list[0] zugreifen wollte. Im Debugger zeigte er mir alle Elemente an, list[1] usw. funktionierte auch. Ich stand erstmal auf dem Schlau. Da ich den Fehler nicht sofort identifizieren konnte, baute ich mir eine kleine Testapplikation, um mit einem Minimum an Code das Problem zu identifizieren.

Letzendlich lag es an einem Indexer, welchen ich selber erstellt habe. Und zwar erwartete dieser als Übergabetyp ein Enum. Dieser überschreibt zwar nicht den von List<T> geerbten Indexer this[int index], jedoch scheint der Compiler hier leichte Probleme zu machen.

Schauen wir uns mal den kompilierten Code in dotPeek an.

      MyTypeList myTypeList1 = new MyTypeList();
      myTypeList1.Add("a");
      myTypeList1.Add("b");
      myTypeList1.Add("c");
      MyTypeList myTypeList2 = myTypeList1;
      if (Enumerable.ElementAt<string>((IEnumerable<string>) myTypeList2, 0) == null)
        throw new NullReferenceException();
      if (myTypeList2[MyEnum.Value1] == null)
        throw new NullReferenceException();

Den ersten Zugriff auf den numerischen Indexer wurde auf den Enum-Indexer geändert. Eine Erklärung dazu konnte ich nicht wirklich finden, bzw. keine die ich nachvollziehen konnte. Letztendlich hat es damit zu tun, dass ein Enum im Bauch eigentlich nur ein int-Typ ist. Dies passiert immer, wenn ich den numerischen Indexer mit 0 zugreife, wohlgemerkt, nur mit 0. Alle anderen Werte funktionieren problemlos.

Wie kann ich das Problem lösen / umgehen?

Dafür gibt es mehrere Wege.

Wird der Index-Wert woher in eine Variable (z.B. innerhalb einer for-Schleife) gesetzt, kann er den korrekten Indexer auflösen.

int index = 0;
if(list[index] == null) {
...
}

Ein Cast direkt auf int hingegen funktioniert nicht (er nutzt wieder den Enum-Indexer):

if(list[(int) 0] == null) {
..
}

Lustiger Weise lässt sich die Solution nicht mehr kompilieren, sobald ich einen weiteren Enum-Indexer hinzufüge:

The call is ambiguous between the following methods or properties: 'DumpSolution.MyTypeList.this[DumpSolution.MyEnum]' and 'DumpSolution.MyTypeList.this[DumpSolution.MyEnum2]'

Die simpelste Lösung ist, einfach den Standard-Indexer zu überschreiben:

    public class MyTypeList : List<string>
    {
        public string this[MyEnum myEnum]
        {
            get { return this.FirstOrDefault(item => item == Enum.GetName(myEnum.GetType(), myEnum)); }
        }

        public string this[MyEnum2 myEnum]
        {
            get { return this.FirstOrDefault(item => item == Enum.GetName(myEnum.GetType(), myEnum)); }
        }

        public new string this[int index]
        {
            get { return base[index]; }
            set { base[index] = value; }
        }
    }

Danach funktioniert der Code oben problemlos und korrekt. Ach ja, ein anpassen des Enums wie folgt brachte auch keinen Erfolg.

enum MyEnum {
   Value1 = 1,
   Value2 = 2
}

Ich kann mir vorstellen, dass es das Problem auch bei anderen Listen gibt, ich selbst habe es aber aktuell nur bei List<T> festgestellt. Im Netz findet man einige Einträge dazu, dazu auch eine Erklärung (welche mich aber nur bedingt zufrieden stellt)

Ich habe ein kleines Beispiel, welches ihr hier herunterladen könnt.

Steam lässt sich nicht starten

Ich hatte eben das Problem, dass sich Steam nicht starten lassen wollte. In der Steam.log stand nur etwas wie

CsComm Session Jun-04-2011 20:02:44.582 [5612] ReconnectThread (5612) Starting

Dieser Eintrag im offiziellen Forum hat mir geholfen, einfach die Datei „ClientRegistry.blob“ im Steam ordner löschen.
Danach ging es wieder.

Update: oder wir starten einfach den Router neu.. Weiß der Geier, was da wieder los war..

Vodafone – The never ending Story

Ich weiß, dass ich euch erst Bilder von unserer Renovierung und dem Umzug versprochen habe, jedoch fehlt mir aktuell die Zeit. Ich muss das nun erstmal alles hier mit Vodafone niederschreiben, auch als persönlich Gedankenstütze!

Ich dachte echt, es wäre alles vorbei. Aber weit weit gefehlt, jetzt scheint der Spaß erst richtig los zu gehen!

Kommunikationsschwierigkeiten?

Am 23. März sollte mein alter Anschluss in Grasleben abgeschaltet werden. Wurde er auch. Zeitgleich klingelte jedoch ein Telekomtechniker in Rhode und wollte DSL schalten. Ich war leider nicht persönlich da, sagte Katrins Eltern aber telefonisch, dass sie ihn gewähren lassen sollen. Fünf Minuten später rief mich der Telekomtechniker selbst an und sagte mir, dass er ja kein DSL schalten brauche, ich hätte ja schon DSL. War ja klar, hatte mir die Telekom ja bereits am 11. März telefonisch mitgeteilt. Er verriet mir noch, dass er im Auftrag von Vodafone da wäre. Also zog er (angeblich) ohne einen Handschlag getan zu haben wieder ab.
Ich verbuchte dies mal unter Kommunikationsschwierigkeiten zwischen Telekom und Vodafone und machte mir keinen weiteren Kopf.

Ein Brief für einen DSL-Kunden

Nun war ich aber heute zufällig in meiner alten Wohnung, wo noch ein paar Briefe im Briefkasten lagen, darunter ein Schreiben von Vodafone vom 29.03.2010.

Sehr geehrter Herr KLEIN,

Sie bekommen heute Ihre neue Festnetz-Nummer zu Ihrem Tarif Vodafone Surf Sofort. [..]

05365 / XXXXXXXX

[..]Als Surf-Sofort-Kunde können Sie diese Rufnummer schon vor der Anschaltung Ihres Festnetz-Anschlusses nutzen.
[..]

Bitte? Ich habe dann sofort bei der Vodafone-Hotline (0800 / 172 12 12, falls mal wer braucht) angerufen und mein Anliegen geschildert. Die Dame sagte mir, ich wäre aktiver DSL-Kunde. Schön, wo ich doch eine schriftliche Kündigung von Vodafone habe. Die Dame empfahl mir, meinem Vertrag mit Hinweis + Kopie der Kündigung sofort zu widersprechen, was ich natürlich morgen auch sofort erledigen werde.

Wenn die Linke nicht mit der Rechten kann – die Timeline

Das nervige sind auch die zwei unterschiedlichen Abteilungen. Ich habe damals ein Surf-Sofort-Paket abgeschlossen. Das heißt, man bekommt ein UMTS-Stick dazu, kann sofort telefonieren und surfen. Ist somit Vodafone Mobil Kunde. Gleichzeitig bin ich aber auch Vodafone DSL Kunde. Ich habe zwei unterschiedliche Kundennummern und die können nie sehen, was bei der jeweiligen anderen abgeht.

Daher mal eine Timeline, wie der genaue Ablauf war. Ich schreibe Vodafone Mobil bzw. Vodafone DSL für die unterschiedlichen Kundennummern, die auf den Schreiben vermerkt waren.

06. Januar 2010:
Ich reiche Online bei Vodafone einen Umzugsauftrag ein.

08. Januar 2010:
Bereits an meine neue und an meine alte Adresse erhalte ich jeweils von Vodafone DSL die neuen Zugangsdaten sowie eine Bestätigung des Anschalttermins für den 23. März 2010, wie gewünscht.

21. Januar 2010:
Ich erhalte von Vodafone Mobil einen Brief. Man könne mir kein DSL schalten und würde mir daher den UMTS-Stick in Rhode anbieten. Genaueres könnt ihr hier nachlesen.

27. Januar 2010:
Obwohl mir der Support nach meinem Telefonat am 21. Januar 2010 zugesichert hat, das mein DSL geschaltet wird, schrieb mir nun Vodafone DSL, dass wie bereits im Schreiben vom 19. Januar (siehe 21. Januar) mitgeteilt, kein DSL geschaltet werden kann (nach zu lesen hier). Ich zitiere:

[..]ist die Schaltung von Vodafone Zuhause DSL, gemäß Rückmeldung der Deutschen Telekom AG, an Ihrem neuen Wohnort leider nicht möglich.

Daher wurde der Umzugsauftrag ARC…. storniert.

Die Kündigung Ihres DSL Anschlusses [..] ( alte Adresse ) wird zum 23. März 2010 wirksam.[..]

Direkt nach diesem Schreiben reichte ich einen Widerspruch des Vodafone Mobile Connect Sofort Vertrags ein, welcher dem Schreiben vom 21. Januar bei lag.

Danach war erstmal lange Zeit Ruhe.

06. April 2010
An meine alte Adresse schrieb Vodafone Mobil, dass ab sofort meine Festnetznummer sowieso geschalten sei. Siehe oben.

Ende vom Lied

Welches Ende? Also ich sehe da noch kein Ende. So langsam geht mir der ganze Kindergarten schon richtig auf den Keks. Wenn das so weiter geht, kann ich ja mal der c’t schreiben, Rubrik “Vorsicht Kunde!”

Dämlicher Programmierfehler

Grad bestimmt 20 Minuten bei der Fehlersuche drauf gegangen. Folgender Code:


List<int> _list = new List<int>();
_list.Add(1);
_list.Add(2);
_list.Add(3);
_list.Add(4);

for(int i = 1; i <= 3; i++) {
   int x = _list[i];
   _list.Remove(x);
}

Mein Code war natürlich nicht mit Integerwerten. Ich musste noch ein paar andere Sachen machen, daher war ein RemoveRange() nicht möglich.
Nun wer findet den Fehler?

Ich liste es einfach mal auf:

Durchlauf: 1 - Lösche Element 1 - Rest 2
Durchlauf: 2 - Lösche Element 2 - Rest 1
Durchlauf: 3 - Lösche Element 3 - Rest 0

Beim letzten Durchlauf wird er immer Probleme kriegen. Das Element gibt es nicht mehr. Durch Remove() rücken die Elemente in der Indizierung nach.
Es müsste also heißen:

Durchlauf: 1 - Lösche Element 1 - Rest 2
Durchlauf: 2 - Lösche Element 1 - Rest 1
Durchlauf: 3 - Lösche Element 1 - Rest 0

oder in C#-Code:

for(int i = 1; i <= 3; i++) {
   int x = _list[1];
   _list.Remove(x);
}

Vor allem wenn man in den Debugger schaut, kamen da merkwürdige Werte raus.
Ich sehe, dass die Methode „DeleteEntries()“ mit dem Count-Parameter 2 aufgerufen wird, oben zeigt mir der Debugger aber 1 an. Liegt daran, dass in der Liste bereits ein Element entfernt wurde. Somit ist also, wenn man es genau nimmt, beides richtig.

C#4.0 – Problematik bei Default Values

Golo Roden erwähnte es bereits in den Kommentaren, daher möchte ich noch mal genauer darauf eingehen.
Dazu erstelle ich eine Solution in Visual Studio 2010 mit einer Konsolenapplikation, welche ich „DefaultValues“ nenne. C#4.0 – Problematik bei Default Values weiterlesen