Vista SP1: Neue Fehlerseite im IIS

April 9, 2008 at 1:04 PMAndre Loker

Nach der Installation von SP1 scheint der IIS neue Fehlerseiten erhalten zu haben. Siehe Bild.

iis404

Posted in: IIS | Windows

Tags: , ,

Vista SP1 beendet Webseiten im IIS

April 9, 2008 at 12:53 PMAndre Loker

Ich habe gerade das Vista SP1 auf meinem Vista Ultimate 64bit installiert. Nach der Installation war die Webseiten im IIS beendet. Es stellte sich heraus, dass der WWW-Publishingdienst nicht lief, weil der dafür benötigte Prozessaktivierungsdienst nicht mehr auf "automatisch starten" eingestellt und entsprechend beendet war.

Wer das SP1 installiert sollte daher kontrollieren, ob der Prozessaktivierungsdienst noch automatisch gestartet wird.

Posted in: IIS | Windows

Tags: , , ,

C# 3.0 - Auto Properties

March 19, 2008 at 11:38 AMAndre Loker

C# 3.0 bietet als Neuerung so genannte Auto Properties. Das sind einfache Properties, für der Compiler automatisch ein Backing Field erstellt. Beispiel:

   1: class MyClass {
   2:     // Auto property 
   3:     public int Position { get; set; }
   4: }

Der compiler erstellt ein implizites Backing Field für Position. Möchte man den Setter vor Zugriffen von außen schützen, ist das auch kein Problem:

   1: class MyClass {
   2:     // Auto property
   3:     public int Position { get; private set; }
   4: }

Der Vorteil dieser Syntax ist die kompakte Schreibweise für triviale Properties. Zudem ist es Problemlos möglich, bei Bedarf die Auto Property in eine vollständige Property zu upgraden bei Beibehaltung der Binärkompatibilität.

Ich stelle allerdings nach einiger Zeit fest, dass diese Syntax für den Leser des Quellcodes verwirrend ist. Ich finde es als Entwickler übersichtlicher, wenn alle Felder zu Beginn der Klasse angegeben sind. Wenn ich Auto Properties verwende, fehlt mir die sofortige Orientierung bzgl. der verwendeten Felder, besonders bei der Vermischung von Auto Properties und expliziten Backing Fields. Da Auto Properties nicht gesondert gekennzeichnet werden, muss ich ggf. über alle Properties schauen, um zu sehen, was wirklich in der Klasse vor sich geht. Es ist übrigens nicht möglich, readonly Felder als Auto Properties darzustellen. Bleibt noch zu resümieren, dass ich Auto Properties - außer bei trivialen Klassen - in Zukunft meiden werde.

Posted in: C#

Tags:

Audiosurf - Wellenritt in der Medienbibliothek

March 2, 2008 at 6:21 PMAndre Loker

audiosurf1Vor einigen Tagen habe ich Audiosurf entdeckt, ein schnelles Geschicklichkeitsspiel, bei dem der Spieler ein Flugobjekt über eine achterbahnartige Strecke steuert, um dabei farbige Blöcke aufzusammeln und zu größeren Einheiten zu kombinieren. Der Clou an der Sache: die Strecken sind nicht etwa einfach so vom Hersteller mitgeliefert, sondern werden zur Laufzeit aus beliebigen Audiodatein (mp3, ogg, Audio-CD u.a.) generiert. Langsame, ruhige Stellen im Stück gibt das Spiel als Steigungen aus, an denen das Raumschiff langsamer fliegt. Schnelle und spannende Teile werden zu mehr oder weniger steilen Gefällen, die man in einer teils rasanten Schussfahrt hinabgleitet. 

Das Primärziel des Spiels ist es, so viele Punkte zu ergattern, wie nur eben möglich. Diese erzielt man in erster Linie durch das Einsammeln gleichfarbiger Blöcke, die sich ebenfalls auf der Fahrbahn bewegen. Berührt man so einen Block, fällt er in ein Spielfeld, dass dem von Vier Gewinnt nicht unähnlich ist. Gelingt es dem Spieler, Gruppen von mindestens drei gleichfarbigen Steinen zu bilden, verschwinden die Blöcke, die Steine darüber fallen hinunter - Tetris lässt grüßen!

Auf dieses Grundprinzip aufbauend, bietet das Spiel verschiedene Spiemodi in drei verschiedenen Schwierigkeitsgraden. Je nach gewähltem "Flugzeug" besitzt der Spieler unterschiedliche Fähigkeiten. So gibt es den Pusher, mit dem Steine auf eine andere "Spur" schieben kann. Beim Spieltyp Ninja gibt es lediglich Blöcke einer Farbe und graue Blöcke, denen es auszuweichen gilt. audiosurf2

Die erreichten Punkte kann man nach dem Run in eine Online-Tabelle eintragen und so mit anderen Benutzern vergleichen. Jedes Lied bekommt dabei eine eigene Tabelle, sodass die Ergebnisse wirklich vergleichbar bleiben.

Trotz - oder gerade weil - seines einfachen Spielprinzips macht Audiosurf unheimlich viel Spaß. Es gehört zu den Titeln, bei dem das "Einer geht noch"-Prinzip voll durchgreift: man spürt den Drang, unbedingt noch eines seiner Lieblingslieder "abzusurfen". Der Titel hat ein enormes Suchtpotenzial. Meiner Meinung nach kann man die rund 8€, die das Spiel inklusive(!) des Orange Box Soundtracks im Moment kostet, kaum besser anlegen, um sich zwischendurch die Zeit zu vertreiben - wenn man es denn dann schafft, sich wieder vom Spiel zu lösen und den "wichtigen" Aufgaben nachzugehen.

Vertrieben wird das englischsprachige Audiosurf ausschließlich über Steam.

Posted in: Off topic

Tags: , ,

Extension methods sind doch irgendwie cool

February 28, 2008 at 2:49 PMAndre Loker

Eigentlich stand ich den Extension Methods von C# 3.0 ja kritisch gegenüber, aber irgendwie sind sie doch ziemlich cool :-)

Hier ein kleines Beispiel aus der Praxis.

   1: /// <summary>
   2: /// Provides extension methods for <see cref="string"/>
   3: /// </summary>
   4: public static class StringExtensions {
   5:     /// <summary>
   6:     /// Calls ToString on the given object if <paramref name="obj"/> is not null; otherwise, 
   7:     /// <see cref="string.Empty"/> is returned.
   8:     /// </summary>
   9:     /// <param name="obj">The obj.</param>
  10:     /// <returns>The result of <see cref="object.ToString()"/> invoked on <paramref name="obj"/> if
  11:     /// <paramref name=" obj"/> is not null; otherwise, <see cref="string.Empty"/></returns>
  12:     public static string ToSafeString<T>( this T obj ) {
  13:         if ( typeof( T ).IsValueType ) {
  14:             return obj.ToString();
  15:         }
  16:         return Equals( obj, null ) ? string.Empty : obj.ToString();
  17:     }
  18:  
  19:     /// <summary>
  20:     /// Formats a string using the given format and a value.
  21:     /// </summary>
  22:     /// <param name="format">The format.</param>
  23:     /// <param name="value">The value.</param>
  24:     /// <returns>The formatted string</returns>
  25:     public static string Formatted<T>( this string format, T value )
  26:         where T : struct {
  27:         return string.Format( format, value.ToSafeString() );
  28:     }
  29:  
  30:     /// <summary>
  31:     /// Formats a string using the given format and the given values
  32:     /// </summary>
  33:     /// <typeparam name="T1">The type of the first argument.</typeparam>
  34:     /// <typeparam name="T2">The type of the second argument.</typeparam>
  35:     /// <param name="format">The format.</param>
  36:     /// <param name="value1">The first value.</param>
  37:     /// <param name="value2">The second value.</param>
  38:     /// <returns>The formatted string</returns>
  39:     public static string Formatted<T1, T2>( this string format, T1 value1, T2 value2 )
  40:         where T1 : struct
  41:         where T2 : struct {
  42:         return string.Format( format, value1.ToSafeString(), value2.ToSafeString() );
  43:     }
  44:  
  45:     /// <summary>
  46:     /// Formats a string using the given format and the given values
  47:     /// </summary>
  48:     /// <typeparam name="T1">The type of the first argument.</typeparam>
  49:     /// <typeparam name="T2">The type of the second argument.</typeparam>
  50:     /// <typeparam name="T3">The type of the third argument.</typeparam>
  51:     /// <param name="format">The format.</param>
  52:     /// <param name="value1">The first value.</param>
  53:     /// <param name="value2">The second value.</param>
  54:     /// <param name="value3">The third value.</param>
  55:     /// <returns>The formatted string</returns>
  56:     public static string Formatted<T1, T2, T3>( this string format, T1 value1, T2 value2, T3 value3 )
  57:         where T1 : struct
  58:         where T2 : struct
  59:         where T3 : struct {
  60:         return string.Format( format, value1.ToSafeString(), value2.ToSafeString(), value3.ToSafeString() );
  61:     }
  62:  
  63:     /// <summary>
  64:     /// Formats a string using the given format and the given values.
  65:     /// </summary>
  66:     /// <param name="format">The format.</param>
  67:     /// <param name="values">The valuess.</param>
  68:     /// <returns>The formatted string</returns>
  69:     public static string Formatted( this string format, object[] values ) {
  70:         return string.Format( format, values );
  71:     }
  72: }

Als Anwendungsbeispiele einige Unit-Tests:

   1: [TestFixture]
   2: public class StringExtensionsTests {
   3:     [Test]
   4:     public void CanFormat() {
   5:         var format = "{0} + {1}";
   6:         var result = format.Formatted( 3, 4 );
   7:         var oldVersion = string.Format( format, 3, 4 );
   8:         Assert.AreEqual( "3 + 4", result );
   9:         Assert.AreEqual( oldVersion, result );
  10:     }
  11:  
  12:     [Test]
  13:     public void ToSafeStringHandlesNull() {
  14:         object x = null;
  15:         Assert.AreEqual( string.Empty, x.ToSafeString() );
  16:     }
  17:  
  18:     [Test]
  19:     public void ToSafeStringHandlesValueType() {
  20:         Assert.AreEqual( "5", 5.ToSafeString() );
  21:     }
  22:  
  23:     [Test]
  24:     public void ToSafeStringHandlesRefType() {
  25:         Assert.AreEqual( "foobar", "foobar".ToSafeString() );
  26:     }
  27: }

Zum einen ist der alternative Syntax kürzer. Es mag hier noch Geschmackssache sein, welche Variante (statische Format-Methode ggü. Extension Method). Die Variante mit den Extension Methods ist allerdings auch ein wenig effizienter. Da die Formatted mit einem, zwei oder drei Parametern generisch sind, findet kein Boxing bei der Übergabe der Argumente statt.

HTML Strings

Bei der Ausgabe von Strings in einem HTML Dokument sollte der Text allein schon aus Sicherheitsgründen HTML-codiert werden, besonders, wenn es sich dabei um Benutzereingaben handelt. Dazu steht die Methode HtmlEncode von HttpUtility bzw. HttpServerUtility bereit. Da ich es lästig finde, stets Server.HtmlEncode(derText) bzw.gar HttpUtility.HtmlEncode(derText) zu verwenden, habe ich eine Klasse geschrieben, die HtmlEncode als Extension-Methode der String-Klasse hinzufügt. Das ganze habe ich noch erweitert mit

  • einem Pendant für UrlEncode
  • den Gegenstücken HtmlDecode und UrlDecode
  • der Methode ToHtmlString, einer Extension für Object, die die Ausgabe von ToString() Html-kodiert (bzw. String.Empty zurückgibt bei einer verwendung mit null)
  • dem ToHtmlString-Pendant für URLs: ToUrlString

Der Code:

   1: /// <summary>
   2: /// Extension methods for <see cref="string"/> and <see cref="object"/> that are useful in the context
   3: /// of web applications.
   4: /// </summary>
   5: public static class WebStringExtensions {
   6:     /// <summary>
   7:     /// HTML encodes the given string.
   8:     /// </summary>
   9:     /// <param name="text">The text.</param>
  10:     /// <returns>The HTML encoded version of <paramref name="text"/></returns>
  11:     public static string HtmlEncode( this string text ) {
  12:         return HttpUtility.HtmlEncode( text );
  13:     }
  14:  
  15:     /// <summary>
  16:     /// Decodes the given HTML encoded string.
  17:     /// </summary>
  18:     /// <param name="text">The text.</param>
  19:     /// <returns>The plain version of the HTML encoded <paramref name="text"/></returns>
  20:     public static string HtmlDecode( this string text ) {
  21:         return HttpUtility.HtmlDecode( text );
  22:     }
  23:  
  24:     /// <summary>
  25:     /// Calls <see cref="object.ToString()"/> on the given object and HTML encodes the result.
  26:     /// </summary>
  27:     /// <param name="item">The item.</param>
  28:     /// <returns>
  29:     /// The HTML encoded version of <paramref name="item"/> or <see cref="string.Empty"/> if
  30:     /// <paramref name="item"/> was null.
  31:     /// </returns>
  32:     public static string ToHtmlString<T>( this T item ) {
  33:         return item.ToSafeString().HtmlEncode();
  34:     }
  35:  
  36:     /// <summary>
  37:     /// Encodes a string to be used inside an url.
  38:     /// </summary>
  39:     /// <param name="text">The text.</param>
  40:     /// <returns>The URLs encoded version of <paramref name="text"/></returns>
  41:     public static string UrlEncode( this string text ) {
  42:         return HttpUtility.UrlEncode( text );
  43:     }
  44:  
  45:     /// <summary>
  46:     /// Decodes a string that was url encoded.
  47:     /// </summary>
  48:     /// <param name="text">The text.</param>
  49:     /// <returns>The plain version of the URL encoded <paramref name="text"/></returns>
  50:     public static string UrlDecode( this string text ) {
  51:         return HttpUtility.UrlDecode( text );
  52:     }
  53:  
  54:     /// <summary>
  55:     /// Calls <see cref="object.ToString()"/> on the given object and HTML encodes the result.
  56:     /// </summary>
  57:     /// <param name="item">The item.</param>
  58:     /// <returns>
  59:     /// The URL encoded version of <paramref name="item"/> or <see cref="string.Empty"/> if
  60:     /// <paramref name="item"/> was null.
  61:     /// </returns>
  62:     public static string ToUrlString<T>( this T item ) {
  63:         return item.ToSafeString().UrlEncode();
  64:     }
  65: }

Hier einige Unit-Tests die direkt die Verwendung der Methoden zeigen:

   1: [TestFixture]
   2: public class WebStringExtensionsTests {
   3:     [Test]
   4:     public void CanHtmlEncode() {
   5:         var original = "für";
   6:         var encoded = original.HtmlEncode();
   7:         var expectedEncoded = "f&#252;r";
   8:         Assert.AreEqual( expectedEncoded, encoded );
   9:     }
  10:  
  11:     [Test]
  12:     public void CanHtmldecode() {
  13:         var original = "f&#252;r";
  14:         var decoded = original.HtmlDecode();
  15:         var expectedDecoded = "für";
  16:         Assert.AreEqual( expectedDecoded, decoded );
  17:     }
  18:  
  19:     [Test]
  20:     public void CanUrlEncode() {
  21:         var original = "für";
  22:         var encoded = original.UrlEncode();
  23:         var expectedEncoded = "f%c3%bcr";
  24:         Assert.AreEqual( expectedEncoded, encoded );
  25:     }
  26:  
  27:     [Test]
  28:     public void CanUrlDecode() {
  29:         var original = "f%c3%bcr";
  30:         var decoded = original.UrlDecode();
  31:         var expectedDecoded = "für";
  32:         Assert.AreEqual( expectedDecoded, decoded );
  33:     }
  34:  
  35:     [Test]
  36:     public void CanUseToHtmlString() {
  37:         var text = new MyCustomObject( 1, 2 ).ToHtmlString();
  38:         Assert.AreEqual( "a &lt;= b", text );
  39:         text = new MyCustomObject( 2, 1 ).ToHtmlString();
  40:         Assert.AreEqual( "a &gt; b", text );
  41:     }
  42:  
  43:     [Test]
  44:     public void CanHandleNullInToHtmlString() {
  45:         object o = null;
  46:         var text = o.ToHtmlString();
  47:         Assert.AreEqual( string.Empty, text );
  48:     }
  49:  
  50:     [Test]
  51:     public void CanUseToUrlString() {
  52:         var text = new MyCustomObject( 1, 2 ).ToUrlString();
  53:         Assert.AreEqual( "a+%3c%3d+b", text );
  54:         text = new MyCustomObject( 2, 1 ).ToUrlString();
  55:         Assert.AreEqual( "a+%3e+b", text );
  56:     }
  57:  
  58:     [Test]
  59:     public void CanHandleNullInToToUrlString() {
  60:         object o = null;
  61:         var text = o.ToUrlString();
  62:         Assert.AreEqual( string.Empty, text );
  63:     }
  64:  
  65:     #region Nested type: MyCustomObject
  66:     private class MyCustomObject {
  67:         private readonly int a;
  68:         private readonly int b;
  69:  
  70:         public MyCustomObject( int a, int b ) {
  71:             this.a = a;
  72:             this.b = b;
  73:         }
  74:  
  75:         public override string ToString() {
  76:             return a > b ? "a > b" : "a <= b";
  77:         }
  78:     }
  79:     #endregion
  80: }

Auch hier arbeiten wir wieder weitestgehend Resourcen schonend. Gerade ToHtmlString und ToUrlString sind im Zuge von Databinding extrem nützlich:

   1: <asp:Label runat="server" ID="label" Text='<%# Eval("UserName").ToHtmlString() %>' />
   2: <asp:HyperLink runat="server" ID="link" NavigateUrl='<%# string.Format("~/Profile.aspx?user={0}", Eval("LoginName").ToUrlString()) %>' />

Posted in: Snippets

Tags: