Kategorie-Archiv: Pitfall

Goto Fail als Lehrstück

Während dem vergangenen Wochenende und dem Anfang dieser Woche wurde der Fehler in der Zertifikats-Überprüfung in Apples Code ausführlich diskutiert. Der Quellcode kann öffentlich angesehen werden und dass die fehlerhafte Codezeile dann auch noch “goto fail” heisst hat natürlich viele ermutigt mit Häme nicht zu sparen.

Goto Fail

Die Code-Zeile, die das Problem verursachte (gelb hervorgehoben)

Eine Variante dieses Fehlers hatte ich auch als Punkt 6 in den 9 unbeliebtesten Entdeckungen in fremdem Code beschrieben.

Ob der Fehler durch ein automatisches Mergen von zwei Codefiles, unkonzentriertes arbeiten beim Code schreiben oder ein unbeabsichtigtes Kopieren der Codezeile entstanden ist kann nur anhand der Codefiles nicht erörtert werden. Gleichwohl darf man aus den Fehlern anderer lernen und so habe ich mir Gedanken gemacht, wie dieser Fehler hätte vermieden werden können.

Hier ein paar Punkte, die aus meiner Sicht das Risiko vermindert hätten:

  • Code Reviews
    Wenn Code Änderungen von einer zweiten Person überprüft werden ist die Gefahr kleiner, dass ein solcher Fehler hinein rutscht. Ausgeschlossen werden kann es natürlich trotzdem nicht, denn Menschen machen Fehler.
  • Warnungen einschalten und beachten
    Da ich die Umgebung nicht kenne, mit der OS X und iOS gebuildet wird kann ich nicht beurteilen, ob der Compiler hier Unterstützung geboten hätte. Aber viele Compiler geben ab einem bestimmten Warning-Level eine Warnung oder sogar einen Fehler aus, wenn Codeabschnitte vorhanden sind, die gar nie erreicht werden können. Dabei müssen dann aber auch die Warnungen entsprechend beachtet werden, nicht wie in Punkt 7 meiner unbeliebtesten Entdeckungen in fremdem Code.
  • Coding Guidelines
    Mit Coding Guidelines, die verlangen, dass jeder Block mit geschweiften Klammern eingefasst wird, hätte dieser Fehler vermieden werden können oder er wäre vermutlich eher aufgefallen. Auch ein fehlerhaftes Mergen mit diesem Resultat wäre vermutlich nicht passiert. Die Guidelines müssen aber auch wieder durch ein Code Review oder durch ein Tool überprüft und durchgesetzt werden.

Fehler passieren, und vielleicht versteckt sich in all den Codezeilen, die ich schon geschrieben habe ein ähnlicher Fehler. Wenn ein solcher Fehler öffentlich wird versuche ich deshalb lieber, daraus zu lernen als über den Verursacher zu spotten.

Welcher Constructor wird aufgerufen?

Image courtesy of "artur84" / FreeDigitalPhotos.net

Image courtesy of “artur84″ / FreeDigitalPhotos.net

Seit der Version 4.0 sind in C# Methoden mit Parametern erlaubt, die einen Default-Wert besitzen. Dies kann auch auf Contructor-Methoden angewendet werden, also z.B.

1
2
3
4
5
6
7
8
9
10
11
12
public class A
{
  public A()
  {
    // ...
  }
 
  public A(string x = "default")
  {
    // ...
  }
}

Die Frage ist nun: Welcher Constructor wird verwendet, wenn ich eine Instanz mit A a = new A(); erzeuge?

Versuch 1

Im oben beschriebenen Fall kann dies schnell beantwortet werden, indem man es einfach ausprobiert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace ConstructorQuiz1
{
  class Program
  {
    static void Main(string[] args)
    {
      A a1 = new A();
      A a2 = new A("fix");
    }
  }
 
  public class A
  {
    public A()
    {
      System.Console.WriteLine("A()");
    }
 
    public A(string x = "default")
    {
      System.Console.WriteLine(x);
    }
  }
}

Und hier das Resultat:

A()
fix

Man sieht also, dass der “am besten passende” Constructor verwendet wird und der Default-Wert keine Verwendung findet. (wenn der Standard-Constructor A() nicht existieren würde, würde natürlich der Default-Wert verwendet).

Versuch 2

Wenn man aber mit den Access Modifiers herumspielt ist das ganze nicht mehr so eindeutig. Setzen wir einmal den Access Modifier des Standard-Constructors auf internal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace ConstructorQuiz2
{
  class Program
  {
    static void Main(string[] args)
    {
      A a1 = new A();
      A a2 = new A("fix");
    }
  }
 
  public class A
  {
    internal A()
    {
      System.Console.WriteLine("A()");
    }
 
    public A(string x = "default")
    {
      System.Console.WriteLine(x);
    }
  }
}

Das Ergebnis ist immer noch das selbe, hier hat der geänderte Access Modifier also noch keine Wirkung gezeigt.

Versuch 3

Versuchen wir jetzt aber mal von Aussen zuzugreifen:

1
2
3
4
5
6
7
8
9
10
11
namespace ConstructorQuiz3
{
  class Program
  {
    static void Main(string[] args)
    {
      ConstructorQuiz2.A a1 = new ConstructorQuiz2.A();
      ConstructorQuiz2.A a2 = new ConstructorQuiz2.A("fix");
    }
  }
}

Dieser Code muss mit einer Referenz auf den compilierten Code aus dem zweiten Beispiel enthalten. Dies erfolgt beim Command Line Tool csc mit dem Parameter /r=<filename>.

Das Resultat sieht nun anders aus:

default
fix

Es wird jetzt also der Constructor mit dem Default-Wert aufgerufen, da der Standard-Constructor von der Dritt-Assembly nicht aufgerufen werden darf.

Fazit

Wann welcher Constructor verwendet wird ist nicht nur von den Parametern sondern auch von den Access Modifiern und dem Kontext abhängig.

Slippery When Wet #6: XPath and the Default Namespace

I proudly present to you the sixth in a infinite number of posts of “Slippery When Wet.” In these posts I show you a little bastard I stumbled on.

Recently, I tried to work on a XmlDocument with XPath in C#. To get the right expressions easily I used the XML Spy from Altova. As soon as I got the right XPath expressions I started Visual Studio to work in my .NET Application. But here, the only expression that worked was “/*”. According to the .NET Documentation the expressions should work, but they didn’t.

So I tried another XML file, one from the examples in the .NET Documentation of XPath. And with this file the (corresponding) expressions worked as expected. So I searched the difference of the two XML files. After a short time I found the relevant difference: The file that worked had no namespace defined, whereas the non-working file had two namespaces. One namespace with a prefix (xmlns:prefix=”URI”) and a default namespaces (xmlns=”URI”).

As I got the reason for my problem I had to look for a solution. A little bit of googling brought me the solution. I had to add the default namespace to the NamespaceManager with a prefix and then use the XPath expressions with the defined prefix.

In .NET 3.5 Microsoft introduced the XDocument class as a alternative (or a replacement) for XmlDocument. The C# code differs a little bit depending on the used class. I found a description when using the XmlDocument class and two when using the XDocument class.

With the added prefix for the default namespace my XPath expressions worked as expected.

Slippery When Wet #5: The Process object or Optimization? don’t do it!

Slippery When WetI proudly present to you the fifth in a infinite number of posts of “Slippery When Wet.” In these posts I show you a little bastard I stumbled on.

To get the memory usage information you can use the System.Diagnostics.Process class. As I wantet to check the memory usage regularly I kept the reference to the Process object in my class. But the value never changed anymore… Then I tried to get the Process object every time i needed the current memory size – and it worked.

So I read the documentation a little more carefully and got to this part:

The process component obtains information about a group of properties all at once. After the Process component has obtained information about one member of any group, it will cache the values for the other properties in that group and not obtain new information about the other members of the group until you call the Refresh method. Therefore, a property value is not guaranteed to be any newer than the last call to the Refresh method. The group breakdowns are operating-system dependent.

So I changed my code again to not get the Process every time I wanted the current memory size but call the Refresh method.

And what did we learn from this episode?

Michael A. Jackson (“The First Rule of Program Optimization: Don’t do it (…)“) and Donald Knuth (“(…) premature optimization is the root of all evil (…)“) are right. Don’t do optimization, especially not if you don’t really know what you are doing…

Slippery When Wet #1: Javascript’s GetMonth()

I proudly present to you the first in a infinite number of posts of “Slippery When Wet.” In these posts I show you a little bastard I stubled on.

I had to reformat a date in Javascript. There were all this little nice functions of a date object like

getFullYear(), getHours(), getMinutes()

and also

getMonth()

. Every of these functions returned the value as it would also be shown, but not getMonth(). getMonth() returns a value from 0 to 11, so 0 is January, 1 is February and so on untill 11, that is December.

I see a possible reason for this: With values from 0 to 11 you can use the value directly as the index for an array with the month names inside, like

alert(months[myDate.getMonth()]);

Exception after sending data async from a thread

If you have a situation like this:

void MyThreadFunc()
{
// do some work

socket.BeginSend(buffer, 0, buffer.Length, 0, new AsyncCallback(MyCallback), s);
}

void MyCallback(IAsyncResult ar)
{
Socket s = (Socket) ar.AsyncState;
s.EndSend(ar);
}

And you send a big buffer over a slow connection it can happen that calling EndSend() will throw a SocketException ‘The I/O operation has been aborted because of either a thread exit or an application request’.

This happens because the Thread that started the sending is no more available when the sending ends. The only solution I know is to make sure the thread is still available or to use syncronous sending if possible.

Exception “No web service found at:” with /js or /jsdebug

With “ASP.NET 2.0 AJAX Extensions 1.0″ (I love this name with two version numbers inside…) you can request a JavaScript Proxy for a web service with appending /js or /jsdebug to the web service path, like “http://my-server/my-service.asmx/js”.

When I deployed my application I’ve got the InvalidOperationException “No web service found at:”. When I called the web service without /js or /jsdebug the web service worked (at least the generated forms in the Browser where shown).

I’ve spent a lot of time ’til I recognized the File my-service.asmx was not included in the deploy package. So without the /js appended the my-service.asmx file was not needed, the code from the compiled dll was executed directly, but when I added /js it needed the file.

Conclusion:
Don’t forget to add the .asmx files in the deployment of your ASP.NET AJAX Project!

Ambiguous match found nach Publishen der ASP.NET Webseite

Beim Entwickeln einer Web-Anwendung bin ich über eine Eigenart von ASP.NET 2.0 gestolpert. Beim Testen auf dem lokalen Rechner lief alles wie gewünscht. Nach dem Publishen der Webseite erhielt ich dann aber den Fehler:

Ambiguous match found

Als Fehlerzeile war die erste Zeile mit der

@Page

-Direktive angegeben.

Nach einigem Suchen im Internet fand ich bei Peter Johnson den Blog-Eintrag Ambiguous match found, der mich auf das Problem aufmerksam machte:

Innerhalb meiner Seite hatte ich ein hiddenField mit der ID

MyIdentifier

und eine

protected

Member-Variable mit dem Namen

myIdentifier

. Diese beiden Variablen lebten während dem Entwickeln in friedlicher Koexistenz, nach dem Publizieren der Webseite vertrugen sie sich aber nicht mehr. Leider liefert das Visual Studio weder während des Entwickelns noch beim Publizieren irgendwelche Warnungen oder Fehler. Nachdem ich

myIdentifier

in

theIdentifier

umbenannt hatte lief dann alles reibunglos.

Fehlendes ImageUrl bei asp:image erzeugt unerwünschtem Seitenaufruf

Stundenlang habe ich gesucht, bis ich den Fehler gefunden habe:

Unter bestimmten Umständen wurde meine Seite bei einem Postback erneut aufgerufen, aber mit

IsPostBack == false

. Bis ich dann herausgefunden habe, dass es am

asp:image

Tag liegt. Wenn das Attribut

ImageUrl

nicht angegeben wird, wird das Tag image mit einem leeren

src

Attribut erzeugt, was den Browser dazu veranlasst, als Bild die Seite laden zu wollen. Dies erzeugte den nicht gewünschten Seitenaufruf. Nachdem ich ein leeres Bild als Standardbild angegeben habe, passiert dieser unerwünschte Seitenaufruf nicht mehr.